From 052ea49884ac5d82a3032200634ddbaab619f01e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 11 Jan 2021 19:24:09 +0100 Subject: [PATCH] Implement magboots. (#2988) Got an alert and action and everything. --- .../HUD/Inventory/ClientInventoryComponent.cs | 49 +++++- .../Components/MagbootsComponent.cs | 22 +++ .../Components/GUI/InventoryComponent.cs | 72 +++++++- .../Components/MagbootsComponent.cs | 157 ++++++++++++++++++ Content.Shared/Actions/ActionType.cs | 1 + Content.Shared/Alert/AlertType.cs | 1 + .../Inventory/SharedInventoryComponent.cs | 6 +- .../MovementSpeedModifierComponent.cs | 13 +- .../Components/SharedMagbootsComponent.cs | 35 ++++ Content.Shared/GameObjects/ContentNetIDs.cs | 1 + Resources/Prototypes/Actions/magboots.yml | 13 ++ Resources/Prototypes/Alerts/alerts.yml | 1 + Resources/Prototypes/Alerts/magboots.yml | 5 + .../Entities/Clothing/Shoes/boots.yml | 12 -- .../Entities/Clothing/Shoes/magboots.yml | 15 ++ 15 files changed, 380 insertions(+), 23 deletions(-) create mode 100644 Content.Client/GameObjects/Components/MagbootsComponent.cs create mode 100644 Content.Server/GameObjects/Components/MagbootsComponent.cs create mode 100644 Content.Shared/GameObjects/Components/SharedMagbootsComponent.cs create mode 100644 Resources/Prototypes/Actions/magboots.yml create mode 100644 Resources/Prototypes/Alerts/magboots.yml create mode 100644 Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml diff --git a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs index d6458ee8f5..a7ac7c22ad 100644 --- a/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs +++ b/Content.Client/GameObjects/Components/HUD/Inventory/ClientInventoryComponent.cs @@ -4,15 +4,13 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Client.GameObjects.Components.Clothing; using Content.Shared.GameObjects.Components.Inventory; -using Content.Shared.GameObjects.Verbs; +using Content.Shared.GameObjects.Components.Movement; using Content.Shared.Preferences.Appearance; -using Robust.Client.Console; using Robust.Client.GameObjects; using Robust.Client.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.ViewVariables; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.Components.Inventory.SharedInventoryComponent.ClientInventoryMessage; @@ -81,6 +79,46 @@ namespace Content.Client.GameObjects.Components.HUD.Inventory return item != null && _slots.Values.Any(e => e == item); } + public override float WalkSpeedModifier + { + get + { + var mod = 1f; + foreach (var slot in _slots.Values) + { + if (slot != null) + { + foreach (var modifier in slot.GetAllComponents()) + { + mod *= modifier.WalkSpeedModifier; + } + } + } + + return mod; + } + } + + public override float SprintSpeedModifier + { + get + { + var mod = 1f; + foreach (var slot in _slots.Values) + { + if (slot != null) + { + foreach (var modifier in slot.GetAllComponents()) + { + mod *= modifier.SprintSpeedModifier; + } + } + } + + return mod; + } + } + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); @@ -120,6 +158,11 @@ namespace Content.Client.GameObjects.Components.HUD.Inventory _slots.Remove(slot); } } + + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? mod)) + { + mod.RefreshMovementSpeedModifiers(); + } } private void _setSlot(Slots slot, IEntity entity) diff --git a/Content.Client/GameObjects/Components/MagbootsComponent.cs b/Content.Client/GameObjects/Components/MagbootsComponent.cs new file mode 100644 index 0000000000..4beda719cc --- /dev/null +++ b/Content.Client/GameObjects/Components/MagbootsComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.GameObjects.Components; +using Robust.Shared.GameObjects; + +#nullable enable + +namespace Content.Client.GameObjects.Components +{ + [RegisterComponent] + public sealed class MagbootsComponent : SharedMagbootsComponent + { + public override bool On { get; set; } + + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) + { + if (curState is not MagbootsComponentState compState) + return; + + On = compState.On; + OnChanged(); + } + } +} diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index f6ea424c1f..3527e9fcfb 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -7,6 +7,7 @@ using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.Interfaces.GameObjects; using Content.Shared.GameObjects.Components.Inventory; +using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.EffectBlocker; @@ -105,6 +106,46 @@ namespace Content.Server.GameObjects.Components.GUI } } + public override float WalkSpeedModifier + { + get + { + var mod = 1f; + foreach (var slot in _slotContainers.Values) + { + if (slot.ContainedEntity != null) + { + foreach (var modifier in slot.ContainedEntity.GetAllComponents()) + { + mod *= modifier.WalkSpeedModifier; + } + } + } + + return mod; + } + } + + public override float SprintSpeedModifier + { + get + { + var mod = 1f; + foreach (var slot in _slotContainers.Values) + { + if (slot.ContainedEntity != null) + { + foreach (var modifier in slot.ContainedEntity.GetAllComponents()) + { + mod *= modifier.SprintSpeedModifier; + } + } + } + + return mod; + } + } + bool IEffectBlocker.CanSlip() { if (Owner.TryGetComponent(out InventoryComponent inventoryComponent) && @@ -165,7 +206,7 @@ namespace Content.Server.GameObjects.Components.GUI return GetSlotItem(slot); } - public IEnumerable LookupItems() where T: Component + public IEnumerable LookupItems() where T : Component { return _slotContainers.Values.SelectMany(x => x.ContainedEntities.Select(e => e.GetComponentOrNull())) .Where(x => x != null); @@ -185,6 +226,7 @@ namespace Content.Server.GameObjects.Components.GUI containedEntity = null; Dirty(); } + return containedEntity?.GetComponent(); } @@ -230,12 +272,16 @@ namespace Content.Server.GameObjects.Components.GUI Dirty(); + UpdateMovementSpeed(); + return true; } - public bool Equip(Slots slot, ItemComponent item, bool mobCheck = true) => Equip(slot, item, mobCheck, out var _); + public bool Equip(Slots slot, ItemComponent item, bool mobCheck = true) => + Equip(slot, item, mobCheck, out var _); - public bool Equip(Slots slot, IEntity entity, bool mobCheck = true) => Equip(slot, entity.GetComponent(), mobCheck); + public bool Equip(Slots slot, IEntity entity, bool mobCheck = true) => + Equip(slot, entity.GetComponent(), mobCheck); /// /// Checks whether an item can be put in the specified slot. @@ -314,9 +360,19 @@ namespace Content.Server.GameObjects.Components.GUI Dirty(); + UpdateMovementSpeed(); + return true; } + private void UpdateMovementSpeed() + { + if (Owner.TryGetComponent(out MovementSpeedModifierComponent mod)) + { + mod.RefreshMovementSpeedModifiers(); + } + } + public void ForceUnequip(Slots slot) { var inventorySlot = _slotContainers[slot]; @@ -452,7 +508,7 @@ namespace Content.Server.GameObjects.Components.GUI var activeHand = hands.GetActiveHand; if (activeHand != null && activeHand.Owner.TryGetComponent(out ItemComponent clothing)) { - hands.Drop(hands.ActiveHand, doDropInteraction:false); + hands.Drop(hands.ActiveHand, doDropInteraction: false); if (!Equip(msg.Inventoryslot, clothing, true, out var reason)) { hands.PutInHand(clothing); @@ -461,6 +517,7 @@ namespace Content.Server.GameObjects.Components.GUI Owner.PopupMessageCursor(reason); } } + break; } case ClientInventoryUpdate.Use: @@ -491,7 +548,9 @@ namespace Content.Server.GameObjects.Components.GUI if (activeHand != null && GetSlotItem(msg.Inventoryslot) == null) { var canEquip = CanEquip(msg.Inventoryslot, activeHand, true, out var reason); - _hoverEntity = new KeyValuePair(msg.Inventoryslot, (activeHand.Owner.Uid, canEquip)); + _hoverEntity = + new KeyValuePair(msg.Inventoryslot, + (activeHand.Owner.Uid, canEquip)); Dirty(); } @@ -519,7 +578,8 @@ namespace Content.Server.GameObjects.Components.GUI } /// - public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null) + public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, + ICommonSession session = null) { base.HandleNetworkMessage(message, netChannel, session); diff --git a/Content.Server/GameObjects/Components/MagbootsComponent.cs b/Content.Server/GameObjects/Components/MagbootsComponent.cs new file mode 100644 index 0000000000..49e6eb25f1 --- /dev/null +++ b/Content.Server/GameObjects/Components/MagbootsComponent.cs @@ -0,0 +1,157 @@ +#nullable enable +using Content.Server.GameObjects.Components.Atmos; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.Components.Mobs; +using Content.Shared.Actions; +using Content.Shared.Alert; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.EntitySystems.ActionBlocker; +using Content.Shared.GameObjects.Verbs; +using Content.Shared.Interfaces.GameObjects.Components; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.ComponentDependencies; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; + +namespace Content.Server.GameObjects.Components +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + public sealed class MagbootsComponent : SharedMagbootsComponent, IUnequipped, IEquipped, IUse, IActivate + { + [ComponentDependency] private ItemComponent? _item = null; + [ComponentDependency] private ItemActionsComponent? _itemActions = null; + [ComponentDependency] private SpriteComponent? _sprite = null; + private bool _on; + + [ViewVariables] + public override bool On + { + get => _on; + set + { + _on = value; + + UpdateContainer(); + _itemActions?.Toggle(ItemActionType.ToggleMagboots, On); + if (_item != null) + _item.EquippedPrefix = On ? "on" : null; + _sprite?.LayerSetState(0, On ? "icon-on" : "icon"); + OnChanged(); + Dirty(); + } + } + + public void Toggle(IEntity user) + { + On = !On; + } + + void IUnequipped.Unequipped(UnequippedEventArgs eventArgs) + { + if (On && eventArgs.Slot == Slots.SHOES) + { + if (eventArgs.User.TryGetComponent(out MovedByPressureComponent? movedByPressure)) + { + movedByPressure.Enabled = true; + } + + if (eventArgs.User.TryGetComponent(out ServerAlertsComponent? alerts)) + { + alerts.ClearAlert(AlertType.Magboots); + } + } + } + + void IEquipped.Equipped(EquippedEventArgs eventArgs) + { + UpdateContainer(); + } + + private void UpdateContainer() + { + if (!Owner.TryGetContainer(out var container)) + return; + + if (container.Owner.TryGetComponent(out InventoryComponent? inventoryComponent) + && inventoryComponent.GetSlotItem(Slots.SHOES)?.Owner == Owner) + { + if (container.Owner.TryGetComponent(out MovedByPressureComponent? movedByPressure)) + { + movedByPressure.Enabled = false; + } + + if (container.Owner.TryGetComponent(out ServerAlertsComponent? alerts)) + { + if (On) + { + alerts.ShowAlert(AlertType.Magboots); + } + else + { + alerts.ClearAlert(AlertType.Magboots); + } + } + } + } + + bool IUse.UseEntity(UseEntityEventArgs eventArgs) + { + Toggle(eventArgs.User); + return true; + } + + void IActivate.Activate(ActivateEventArgs eventArgs) + { + Toggle(eventArgs.User); + } + + public override ComponentState GetComponentState() + { + return new MagbootsComponentState(On); + } + + [UsedImplicitly] + public sealed class ToggleMagbootsVerb : Verb + { + protected override void GetData(IEntity user, MagbootsComponent component, VerbData data) + { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + + data.Text = Loc.GetString("Toggle Magboots"); + } + + protected override void Activate(IEntity user, MagbootsComponent component) + { + component.Toggle(user); + } + } + } + + [UsedImplicitly] + public sealed class ToggleMagbootsAction : IToggleItemAction + { + public void ExposeData(ObjectSerializer serializer) { } + + public bool DoToggleAction(ToggleItemActionEventArgs args) + { + if (!args.Item.TryGetComponent(out var magboots)) + return false; + + magboots.Toggle(args.Performer); + return true; + } + } +} diff --git a/Content.Shared/Actions/ActionType.cs b/Content.Shared/Actions/ActionType.cs index 90160db3d4..80690d8117 100644 --- a/Content.Shared/Actions/ActionType.cs +++ b/Content.Shared/Actions/ActionType.cs @@ -24,6 +24,7 @@ Error, ToggleInternals, ToggleLight, + ToggleMagboots, DebugInstant, DebugToggle, DebugTargetPoint, diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 95da080a50..a4962a1d36 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -45,6 +45,7 @@ Parched, Pulled, Pulling, + Magboots, Debug1, Debug2, Debug3, diff --git a/Content.Shared/GameObjects/Components/Inventory/SharedInventoryComponent.cs b/Content.Shared/GameObjects/Components/Inventory/SharedInventoryComponent.cs index 608941b390..90dcf2d808 100644 --- a/Content.Shared/GameObjects/Components/Inventory/SharedInventoryComponent.cs +++ b/Content.Shared/GameObjects/Components/Inventory/SharedInventoryComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Content.Shared.GameObjects.Components.Movement; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Reflection; @@ -11,7 +12,7 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine namespace Content.Shared.GameObjects.Components.Inventory { - public abstract class SharedInventoryComponent : Component + public abstract class SharedInventoryComponent : Component, IMoveSpeedModifier { // ReSharper disable UnassignedReadonlyField [Dependency] protected readonly IReflectionManager ReflectionManager; @@ -100,5 +101,8 @@ namespace Content.Shared.GameObjects.Components.Inventory Slot = slot; } } + + public abstract float WalkSpeedModifier { get; } + public abstract float SprintSpeedModifier { get; } } } diff --git a/Content.Shared/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs b/Content.Shared/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs index dd9a1e391a..a978f31769 100644 --- a/Content.Shared/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs +++ b/Content.Shared/GameObjects/Components/Movement/MovementSpeedModifierComponent.cs @@ -1,4 +1,6 @@ -using Robust.Shared.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -54,6 +56,15 @@ namespace Content.Shared.GameObjects.Components.Movement _movespeedModifiersNeedRefresh = true; } + public static void RefreshItemModifiers(IEntity item) + { + if (item.TryGetContainer(out var container) && + container.Owner.TryGetComponent(out MovementSpeedModifierComponent mod)) + { + mod.RefreshMovementSpeedModifiers(); + } + } + public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); diff --git a/Content.Shared/GameObjects/Components/SharedMagbootsComponent.cs b/Content.Shared/GameObjects/Components/SharedMagbootsComponent.cs new file mode 100644 index 0000000000..02f19e4350 --- /dev/null +++ b/Content.Shared/GameObjects/Components/SharedMagbootsComponent.cs @@ -0,0 +1,35 @@ +using System; +using Content.Shared.GameObjects.Components.Movement; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + public abstract class SharedMagbootsComponent : Component, IMoveSpeedModifier + { + public sealed override string Name => "Magboots"; + public sealed override uint? NetID => ContentNetIDs.MAGBOOTS; + + public abstract bool On { get; set; } + + + protected void OnChanged() + { + MovementSpeedModifierComponent.RefreshItemModifiers(Owner); + } + + public float WalkSpeedModifier => On ? 0.85f : 1; + public float SprintSpeedModifier => On ? 0.65f : 1; + + [Serializable, NetSerializable] + public sealed class MagbootsComponentState : ComponentState + { + public bool On { get; } + + public MagbootsComponentState(bool @on) : base(ContentNetIDs.MAGBOOTS) + { + On = on; + } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 3c54d324ed..0fe7b87c6c 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -88,6 +88,7 @@ public const uint REAGENT_GRINDER = 1082; public const uint ACTIONS = 1083; public const uint DAMAGEABLE = 1084; + public const uint MAGBOOTS = 1085; // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Resources/Prototypes/Actions/magboots.yml b/Resources/Prototypes/Actions/magboots.yml new file mode 100644 index 0000000000..1002510087 --- /dev/null +++ b/Resources/Prototypes/Actions/magboots.yml @@ -0,0 +1,13 @@ +- type: itemAction + actionType: ToggleMagboots + icon: Clothing/Shoes/Boots/magboots.rsi/icon.png + iconOn: Clothing/Shoes/Boots/magboots.rsi/icon-on.png + name: "Toggle Magboots" + description: "Turn your magboots on." + filters: + - tools + keywords: + - atmos + - air + behaviorType: Toggle + behavior: !type:ToggleMagbootsAction { } diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index a12d8ccca7..d8f71e1d8e 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -15,6 +15,7 @@ - category: Temperature - category: Hunger - category: Thirst + - alertType: Magboots - type: alert alertType: LowOxygen diff --git a/Resources/Prototypes/Alerts/magboots.yml b/Resources/Prototypes/Alerts/magboots.yml new file mode 100644 index 0000000000..a39374c762 --- /dev/null +++ b/Resources/Prototypes/Alerts/magboots.yml @@ -0,0 +1,5 @@ +- type: alert + alertType: Magboots + icon: { sprite: "/Textures/Clothing/Shoes/Boots/magboots.rsi", state: "icon-on" } + name: "Magboots" + description: You are immume to airflow, but slightly slower. diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml index b007d8e761..7600d0814d 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml @@ -1,15 +1,3 @@ -- type: entity - parent: ClothingShoesBase - id: ClothingShoesBootsMag - name: magboots - description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. - components: - - type: Sprite - sprite: Clothing/Shoes/Boots/magboots.rsi - state: icon - - type: Clothing - sprite: Clothing/Shoes/Boots/magboots.rsi - - type: entity parent: ClothingShoesBase id: ClothingShoesBootsWork diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml new file mode 100644 index 0000000000..11a647d703 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -0,0 +1,15 @@ +- type: entity + parent: ClothingShoesBase + id: ClothingShoesBootsMag + name: magboots + description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. + components: + - type: Sprite + sprite: Clothing/Shoes/Boots/magboots.rsi + state: icon + - type: Clothing + sprite: Clothing/Shoes/Boots/magboots.rsi + - type: Magboots + - type: ItemActions + actions: + - actionType: ToggleMagboots