diff --git a/Content.Client/ADT/Animations/FlipOnHitSystem.cs b/Content.Client/ADT/Animations/FlipOnHitSystem.cs new file mode 100644 index 00000000000..4bf491ad406 --- /dev/null +++ b/Content.Client/ADT/Animations/FlipOnHitSystem.cs @@ -0,0 +1,76 @@ +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Shared.Animations; +using Robust.Shared.Timing; +using Content.Shared.Animations; + +namespace Content.Client.Animations; + +public sealed class FlipOnHitSystem : SharedFlipOnHitSystem +{ + [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAnimationComplete); + SubscribeAllEvent(ev => PlayAnimation(GetEntity(ev.User))); + } + + private void OnAnimationComplete(Entity ent, ref AnimationCompletedEvent args) + { + if (args.Key != FlippingComponent.AnimationKey) + return; + + PlayAnimation(ent); + } + + protected override void PlayAnimation(EntityUid user) + { + if (!_timing.IsFirstTimePredicted) + return; + + if (TerminatingOrDeleted(user)) + return; + + if (_animationSystem.HasRunningAnimation(user, FlippingComponent.AnimationKey)) + { + EnsureComp(user); + return; + } + + RemComp(user); + + var baseAngle = Angle.Zero; + if (EntityManager.TryGetComponent(user, out SpriteComponent? sprite)) + baseAngle = sprite.Rotation; + + var degrees = baseAngle.Degrees; + + var animation = new Animation + { + Length = TimeSpan.FromMilliseconds(500), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(SpriteComponent), + Property = nameof(SpriteComponent.Rotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees), 0f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees + 180), 0.25f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees + 360), 0.25f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees), 0f), + } + } + } + }; + + + _animationSystem.Play(user, animation, FlippingComponent.AnimationKey); + } +} diff --git a/Content.Client/ADT/Animations/FlippingComponent.cs b/Content.Client/ADT/Animations/FlippingComponent.cs new file mode 100644 index 00000000000..0d0f6ade090 --- /dev/null +++ b/Content.Client/ADT/Animations/FlippingComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Client.Animations; + +[RegisterComponent] +public sealed partial class FlippingComponent : Component +{ + public const string AnimationKey = "flip"; +} diff --git a/Content.Client/ADT/MiningShop/MiningShopBui.cs b/Content.Client/ADT/MiningShop/MiningShopBui.cs new file mode 100644 index 00000000000..483703e4b2f --- /dev/null +++ b/Content.Client/ADT/MiningShop/MiningShopBui.cs @@ -0,0 +1,221 @@ +using System.Linq; +using Content.Shared.ADT.MiningShop; +using Content.Shared.Mind; +using Content.Shared.ADT.Salvage.Systems; +using Content.Shared.Roles.Jobs; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Player; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using static System.StringComparison; +using static Robust.Client.UserInterface.Controls.LineEdit; + +namespace Content.Client.ADT.MiningShop; + +[UsedImplicitly] +public sealed class MiningShopBui : BoundUserInterface +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IResourceCache _resource = default!; + private readonly MiningPointsSystem _miningPoints; + private MiningShopWindow? _window; + public MiningShopBui(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _miningPoints = EntMan.System(); + } + + protected override void Open() + { + _window = new MiningShopWindow(); + _window.OnClose += Close; + _window.Title = EntMan.GetComponentOrNull(Owner)?.EntityName ?? "MiningShop"; + + if (EntMan.TryGetComponent(Owner, out MiningShopComponent? vendor)) + { + for (var sectionIndex = 0; sectionIndex < vendor.Sections.Count; sectionIndex++) + { + var section = vendor.Sections[sectionIndex]; + + var uiSection = new MiningShopSection(); + uiSection.Label.SetMessage(GetSectionName(section)); + + + for (var entryIndex = 0; entryIndex < section.Entries.Count; entryIndex++) + { + var entry = section.Entries[entryIndex]; + var uiEntry = new MiningShopEntry(); + + if (_prototype.TryIndex(entry.Id, out var entity)) + { + uiEntry.Texture.Textures = SpriteComponent.GetPrototypeTextures(entity, _resource) + .Select(o => o.Default) + .ToList(); + uiEntry.Panel.Button.Label.Text = entry.Name?.Replace("\\n", "\n") ?? entity.Name; + + var name = entity.Name; + var color = MiningShopPanel.DefaultColor; + var borderColor = MiningShopPanel.DefaultBorderColor; + var hoverColor = MiningShopPanel.DefaultBorderColor; + + uiEntry.Panel.Color = color; + uiEntry.Panel.BorderColor = borderColor; + uiEntry.Panel.HoveredColor = hoverColor; + + var msg = new FormattedMessage(); + msg.AddText(name); + msg.PushNewline(); + + if (!string.IsNullOrWhiteSpace(entity.Description)) + msg.AddText(entity.Description); + + var tooltip = new Tooltip(); + tooltip.SetMessage(msg); + + uiEntry.TooltipLabel.ToolTip = entity.Description; + uiEntry.TooltipLabel.TooltipDelay = 0; + uiEntry.TooltipLabel.TooltipSupplier = _ => tooltip; + + var sectionI = sectionIndex; + var entryI = entryIndex; + uiEntry.Panel.Button.OnPressed += _ => OnButtonPressed(sectionI, entryI); + } + + uiSection.Entries.AddChild(uiEntry); + } + + _window.Sections.AddChild(uiSection); + } + } + _window.Express.OnPressed += _ => OnExpressDeliveryButtonPressed(); + _window.Search.OnTextChanged += OnSearchChanged; + + Refresh(); + + _window.OpenCentered(); + } + + private void OnButtonPressed(int sectionIndex, int entryIndex) + { + var msg = new MiningShopBuiMsg(sectionIndex, entryIndex); + SendMessage(msg); + Refresh(); + } + + private void OnExpressDeliveryButtonPressed() + { + var msg = new MiningShopExpressDeliveryBuiMsg(); + SendMessage(msg); + Refresh(); + } + + private void OnSearchChanged(LineEditEventArgs args) + { + if (_window == null) + return; + + foreach (var sectionControl in _window.Sections.Children) + { + if (sectionControl is not MiningShopSection section) + continue; + + var any = false; + foreach (var entriesControl in section.Entries.Children) + { + if (entriesControl is not MiningShopEntry entry) + continue; + + if (string.IsNullOrWhiteSpace(args.Text)) + entry.Visible = true; + else + entry.Visible = entry.Panel.Button.Label.Text?.Contains(args.Text, OrdinalIgnoreCase) ?? false; + + if (entry.Visible) + any = true; + } + + section.Visible = any; + } + } + + public void Refresh() + { + if (_window == null || _player.LocalEntity == null) + return; + + if (!EntMan.TryGetComponent(Owner, out MiningShopComponent? vendor)) + return; + + List names = new List(); + + foreach (var order in vendor.OrderList) + { + var name = _prototype.TryIndex(order.Id, out var entity) ? entity.Name : order.Name; + if (name != null) + names.Add(name); + } + var orders = string.Join(", ", names); + + var userpoints = _miningPoints.TryFindIdCard(_player.LocalEntity.Value)?.Comp?.Points ?? 0; + + _window.YourPurchases.Text = $"Заказы: {orders}"; + + _window.Express.Text = $"Экспресс доставка"; + + _window.PointsLabel.Text = $"Осталось очков: {userpoints}"; + + for (var sectionIndex = 0; sectionIndex < vendor.Sections.Count; sectionIndex++) + { + var section = vendor.Sections[sectionIndex]; + var uiSection = (MiningShopSection) _window.Sections.GetChild(sectionIndex); + uiSection.Label.SetMessage(GetSectionName(section)); + + var sectionDisabled = false; + + for (var entryIndex = 0; entryIndex < section.Entries.Count; entryIndex++) + { + var entry = section.Entries[entryIndex]; + var uiEntry = (MiningShopEntry) uiSection.Entries.GetChild(entryIndex); + var disabled = sectionDisabled; + + if (userpoints < entry.Price) + { + disabled = true; + } + + uiEntry.Price.Text = $"{entry.Price}P"; + + uiEntry.Panel.Button.Disabled = disabled; + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _window?.Dispose(); + } + + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + switch (message) + { + case MiningShopRefreshBuiMsg: + Refresh(); + break; + } + } + + private FormattedMessage GetSectionName(SharedMiningShopSection section) + { + var name = new FormattedMessage(); + name.PushTag(new MarkupNode("bold", new MarkupParameter(section.Name.ToUpperInvariant()), null)); + name.AddText(section.Name.ToUpperInvariant()); + + name.Pop(); + return name; + } +} diff --git a/Content.Client/ADT/MiningShop/MiningShopButton.xaml b/Content.Client/ADT/MiningShop/MiningShopButton.xaml new file mode 100644 index 00000000000..0b4faeac7c2 --- /dev/null +++ b/Content.Client/ADT/MiningShop/MiningShopButton.xaml @@ -0,0 +1,7 @@ + + + + diff --git a/Content.Client/ADT/MiningShop/MiningShopButton.xaml.cs b/Content.Client/ADT/MiningShop/MiningShopButton.xaml.cs new file mode 100644 index 00000000000..28c60b97223 --- /dev/null +++ b/Content.Client/ADT/MiningShop/MiningShopButton.xaml.cs @@ -0,0 +1,22 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.ADT.MiningShop; + +[GenerateTypedNameReferences] +public sealed partial class MiningShopButton : Button +{ + public event Action? OnDrawModeChanged; + + public MiningShopButton() + { + RobustXamlLoader.Load(this); + } + + protected override void DrawModeChanged() + { + OnDrawModeChanged?.Invoke(); + } +} + diff --git a/Content.Client/ADT/MiningShop/MiningShopEntry.xaml b/Content.Client/ADT/MiningShop/MiningShopEntry.xaml new file mode 100644 index 00000000000..0dd760fa904 --- /dev/null +++ b/Content.Client/ADT/MiningShop/MiningShopEntry.xaml @@ -0,0 +1,13 @@ + + + +