From 895c546a99b19b6780f3aac70bc4b10900731016 Mon Sep 17 00:00:00 2001 From: Remuchi Date: Fri, 26 Jan 2024 16:08:08 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BF=D1=80=D0=BE=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=85=D0=B8=D1=80=D1=83=D1=80=D0=B3=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit автор Грабля --- .../White/CheapSurgery/CheapSurgerySystem.cs | 49 +++++++ .../Controls/RadialContainer.xaml.cs | 112 +++++++++------- .../White/CheapSurgery/CheapSurgerySystem.cs | 122 ++++++++++++++++++ .../White/Construction/Completions/Surgery.cs | 28 ++++ .../CheapSurgery/ActiveSurgeryComponent.cs | 7 + .../White/CheapSurgery/OnSurgeryStarted.cs | 44 +++++++ .../CheapSurgery/SharedCheapSurgerySystem.cs | 9 ++ .../Objects/Specific/Medical/surgery.yml | 2 +- .../Construction/Surgery/body_surgery.yml | 21 +++ 9 files changed, 347 insertions(+), 47 deletions(-) create mode 100644 Content.Client/White/CheapSurgery/CheapSurgerySystem.cs create mode 100644 Content.Server/White/CheapSurgery/CheapSurgerySystem.cs create mode 100644 Content.Server/White/Construction/Completions/Surgery.cs create mode 100644 Content.Shared/White/CheapSurgery/ActiveSurgeryComponent.cs create mode 100644 Content.Shared/White/CheapSurgery/OnSurgeryStarted.cs create mode 100644 Content.Shared/White/CheapSurgery/SharedCheapSurgerySystem.cs create mode 100644 Resources/Prototypes/White/Construction/Surgery/body_surgery.yml diff --git a/Content.Client/White/CheapSurgery/CheapSurgerySystem.cs b/Content.Client/White/CheapSurgery/CheapSurgerySystem.cs new file mode 100644 index 0000000000..7891242fa7 --- /dev/null +++ b/Content.Client/White/CheapSurgery/CheapSurgerySystem.cs @@ -0,0 +1,49 @@ +using Content.Client.White.UserInterface.Controls; +using Content.Shared.White.CheapSurgery; +using Robust.Client.GameObjects; + +namespace Content.Client.White.CheapSurgery; + +public sealed class CheapSurgerySystem : SharedCheapSurgerySystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeNetworkEvent(OnStarted); + } + + private void OnStarted(OnSurgeryStarted ev) + { + OpenRadialMenu(ev.OrganItems); + } + + public void OpenRadialMenu(List items) + { + if (items.Count == 0) + return; + + var radialContainer = new RadialContainer(); + foreach (var organ in items) + { + var radialButton = radialContainer.AddButton(organ.Name, _sprite.Frame0(organ.Icon)); + radialButton.Controller.OnPressed += _ => + { + radialContainer.Close(); + if (organ.Children.Count > 0) + OpenRadialMenu(organ.Children); + else + SelectOrgan(GetEntity(organ.Uid)); + }; + } + + radialContainer.OpenCentered(); + } + + public void SelectOrgan(EntityUid uid) + { + var ev = new OnOrganSelected(GetNetEntity(uid)); + RaiseNetworkEvent(ev); + } +} diff --git a/Content.Client/White/UserInterface/Controls/RadialContainer.xaml.cs b/Content.Client/White/UserInterface/Controls/RadialContainer.xaml.cs index d9678af38d..2cf2cafe5d 100644 --- a/Content.Client/White/UserInterface/Controls/RadialContainer.xaml.cs +++ b/Content.Client/White/UserInterface/Controls/RadialContainer.xaml.cs @@ -6,6 +6,7 @@ using Content.Client.Viewport; using JetBrains.Annotations; using Robust.Client.Animations; using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Client.ResourceManagement; @@ -31,10 +32,10 @@ public sealed class RadialContainerCommandTest : LocalizedCommands for (int i = 0; i < 24; i++) { var testButton = radial.AddButton("Action " + i, "/Textures/Interface/emotions.svg.192dpi.png"); - testButton.Controller.OnPressed += (_) => { Logger.Debug("Press gay"); }; + testButton.Controller.OnPressed += _ => { Logger.Debug("Press gay"); }; } - radial.CloseButton.Controller.OnPressed += (_) => + radial.CloseButton.Controller.OnPressed += _ => { Logger.Debug("Close event for your own logic"); }; @@ -46,17 +47,16 @@ public sealed class RadialContainerCommandTest : LocalizedCommands [GenerateTypedNameReferences, Virtual] public partial class RadialContainer : Control { - private bool _isOpened = false; + private bool _isOpened; - private Vector2 _focusSize = new Vector2(64, 64); - private Vector2 _normalSize = new Vector2(50, 50); + private Vector2 _focusSize = new(64, 64); + private Vector2 _normalSize = new(50, 50); - private float _moveAniTime = 0.3f; - private float _focusAniTime = 0.25f; + private IResourceCache _resourceCache; - private int _maxButtons = 8; + private const int MaxButtons = 8; - private string _backgroundTexture = "/Textures/Interface/Default/SlotBackground.png"; + private const string BackgroundTexture = "/Textures/Interface/Default/SlotBackground.png"; public const string MoveAnimationKey = "move"; public const string InSizeAnimationKey = "insize"; @@ -67,29 +67,24 @@ public partial class RadialContainer : Control get => _focusSize.Y; set => _focusSize = new Vector2(value, value); } + public float NormalSize { get => _normalSize.Y; set => _normalSize = new Vector2(value, value); } - public float MoveAnimationTime - { - get => _moveAniTime; - set => _moveAniTime = value; - } - public float FocusAnimationTime - { - get => _focusAniTime; - set => _focusAniTime = value; - } + public float MoveAnimationTime { get; set; } = 0.3f; + + public float FocusAnimationTime { get; set; } = 0.25f; public bool IsAction = true; - public RadialContainer() : base() + public RadialContainer() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); + _resourceCache = IoCManager.Resolve(); } public void Open(Vector2 position) @@ -103,9 +98,10 @@ public partial class RadialContainer : Control { AddToRoot(); if (Parent != null) - LayoutContainer.SetPosition(this, (Parent.Size/2) - (this.Size/2)); + LayoutContainer.SetPosition(this, Parent.Size / 2 - Size / 2); else - LayoutContainer.SetPosition(this, (UserInterfaceManager.MainViewport.Size/2) - (this.Size/2)); + LayoutContainer.SetPosition(this, UserInterfaceManager.MainViewport.Size / 2 - Size / 2); + UpdateButtons(); } @@ -114,7 +110,7 @@ public partial class RadialContainer : Control if (UserInterfaceManager.ActiveScreen == null) return; - var ent = IoCManager.Resolve().LocalPlayer?.ControlledEntity; + var ent = IoCManager.Resolve().LocalSession?.AttachedEntity; if (ent == null) return; @@ -122,7 +118,11 @@ public partial class RadialContainer : Control return; AddToRoot(); - LayoutContainer.SetPosition(this, (IoCManager.Resolve().MapToScreen(xform.MapPosition).Position * 1.5f)); + var position = IoCManager.Resolve().GetMapCoordinates(xform); + + LayoutContainer.SetPosition(this, + IoCManager.Resolve().MapToScreen(position).Position * 1.5f); + UpdateButtons(); } @@ -139,9 +139,21 @@ public partial class RadialContainer : Control { var button = new RadialButton(); button.Content = action; - button.Controller.TextureNormal = IoCManager.Resolve().GetTexture(_backgroundTexture); + button.Controller.TextureNormal = _resourceCache.GetTexture(BackgroundTexture); if (texture != null) - button.BackgroundTexture.Texture = IoCManager.Resolve().GetTexture(texture); + button.BackgroundTexture.Texture = _resourceCache.GetTexture(texture); + + Layout.AddChild(button); + + return button; + } + + public RadialButton AddButton(string action, Texture texture) + { + var button = new RadialButton(); + button.Content = action; + button.Controller.TextureNormal = _resourceCache.GetTexture(BackgroundTexture); + button.BackgroundTexture.Texture = texture; Layout.AddChild(button); return button; @@ -151,6 +163,7 @@ public partial class RadialContainer : Control { if (_isOpened) return; + UserInterfaceManager.WindowRoot.AddChild(this); _isOpened = !_isOpened; } @@ -159,31 +172,33 @@ public partial class RadialContainer : Control { Visible = true; - var angleDegrees = 360/Layout.ChildCount; + var angleDegrees = 360 / Layout.ChildCount; var stepAngle = -angleDegrees + -90; var distance = FocusSize * 1.2; - if (Layout.Children.Count() > _maxButtons) + if (Layout.Children.Count() > MaxButtons) { - for (int i = 0; i < (Layout.Children.Count() - _maxButtons); i++) + for (int i = 0; i < (Layout.Children.Count() - MaxButtons); i++) { - distance += (NormalSize/3); + distance += (NormalSize / 3); } } + foreach (var child in Layout.Children) { - var button = (RadialButton)child; + var button = (RadialButton) child; button.ButtonSize = _normalSize; stepAngle += angleDegrees; var pos = GetPointFromPolar(stepAngle, distance); PlayRadialAnimation(button, pos, MoveAnimationKey); - button.Controller.OnMouseEntered += (_) => + button.Controller.OnMouseEntered += _ => { PlaySizeAnimation(button, _focusSize, OutSizeAnimationKey, InSizeAnimationKey); ActionLabel.Text = button.Content ?? string.Empty; ActionLabel.Visible = IsAction; }; - button.Controller.OnMouseExited += (_) => + + button.Controller.OnMouseExited += _ => { PlaySizeAnimation(button, _normalSize, InSizeAnimationKey, OutSizeAnimationKey); ActionLabel.Visible = false; @@ -191,18 +206,20 @@ public partial class RadialContainer : Control } CloseButton.ButtonSize = _normalSize; - CloseButton.Controller.OnMouseEntered += (_) => + CloseButton.Controller.OnMouseEntered += _ => { PlaySizeAnimation(CloseButton, _focusSize, OutSizeAnimationKey, InSizeAnimationKey); ActionLabel.Text = CloseButton.Content ?? string.Empty; ActionLabel.Visible = true; }; - CloseButton.Controller.OnMouseExited += (_) => + + CloseButton.Controller.OnMouseExited += _ => { PlaySizeAnimation(CloseButton, _normalSize, InSizeAnimationKey, OutSizeAnimationKey); ActionLabel.Visible = false; }; - CloseButton.Controller.OnPressed += (_) => + + CloseButton.Controller.OnPressed += _ => { Close(); }; @@ -212,7 +229,7 @@ public partial class RadialContainer : Control { var anim = new Animation { - Length = TimeSpan.FromMilliseconds(_moveAniTime * 1000), + Length = TimeSpan.FromMilliseconds(MoveAnimationTime * 1000), AnimationTracks = { new AnimationTrackControlProperty @@ -221,12 +238,13 @@ public partial class RadialContainer : Control InterpolationMode = AnimationInterpolationMode.Linear, KeyFrames = { - new AnimationTrackProperty.KeyFrame(new Vector2(0,0), 0f), - new AnimationTrackProperty.KeyFrame(pos, _moveAniTime) + new AnimationTrackProperty.KeyFrame(new Vector2(0, 0), 0f), + new AnimationTrackProperty.KeyFrame(pos, MoveAnimationTime) } } } }; + if (!button.HasRunningAnimation(playKey)) button.PlayAnimation(anim, playKey); } @@ -235,7 +253,7 @@ public partial class RadialContainer : Control { var anim = new Animation { - Length = TimeSpan.FromMilliseconds(_focusAniTime * 1000), + Length = TimeSpan.FromMilliseconds(FocusAnimationTime * 1000), AnimationTracks = { new AnimationTrackControlProperty @@ -245,7 +263,7 @@ public partial class RadialContainer : Control KeyFrames = { new AnimationTrackProperty.KeyFrame(button.Size, 0f), - new AnimationTrackProperty.KeyFrame(size, _focusAniTime) + new AnimationTrackProperty.KeyFrame(size, FocusAnimationTime) } } } @@ -253,6 +271,7 @@ public partial class RadialContainer : Control if (stopKey != null && button.HasRunningAnimation(stopKey)) button.StopAnimation(stopKey); + if (!button.HasRunningAnimation(playKey)) button.PlayAnimation(anim, playKey); } @@ -263,11 +282,12 @@ public partial class RadialContainer : Control foreach (var child in Layout.Children) { - var button = (RadialButton)child; - LayoutContainer.SetPosition(child, button.Offset - (button.Size/2)); + var button = (RadialButton) child; + LayoutContainer.SetPosition(child, button.Offset - (button.Size / 2)); } - LayoutContainer.SetPosition(CloseButton, CloseButton.Offset - (CloseButton.Size/2)); - LayoutContainer.SetPosition(ActionBox, new Vector2(0 - (ActionLabel.Size.X), FocusSize*1.5f)); + + LayoutContainer.SetPosition(CloseButton, CloseButton.Offset - (CloseButton.Size / 2)); + LayoutContainer.SetPosition(ActionBox, new Vector2(0 - (ActionLabel.Size.X), FocusSize * 1.5f)); } private static Vector2 GetPointFromPolar(double angleDegrees, double distance) @@ -277,6 +297,6 @@ public partial class RadialContainer : Control var x = distance * Math.Cos(angleRadians); var y = distance * Math.Sin(angleRadians); - return new Vector2((int)Math.Round(x), (int)Math.Round(y)); + return new Vector2((int) Math.Round(x), (int) Math.Round(y)); } } diff --git a/Content.Server/White/CheapSurgery/CheapSurgerySystem.cs b/Content.Server/White/CheapSurgery/CheapSurgerySystem.cs new file mode 100644 index 0000000000..62f3529ccb --- /dev/null +++ b/Content.Server/White/CheapSurgery/CheapSurgerySystem.cs @@ -0,0 +1,122 @@ +using Content.Server.Body.Systems; +using Content.Server.Construction; +using Content.Server.Construction.Components; +using Content.Server.Kitchen.Components; +using Content.Shared.Body.Components; +using Content.Shared.Body.Organ; +using Content.Shared.Body.Part; +using Content.Shared.Humanoid; +using Content.Shared.Interaction; +using Content.Shared.Mobs.Systems; +using Content.Shared.White.CheapSurgery; +using Robust.Shared.Utility; + +namespace Content.Server.White.CheapSurgery; + +public sealed class CheapSurgerySystem : SharedCheapSurgerySystem +{ + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly ConstructionSystem _construction = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnUsing); + SubscribeNetworkEvent(OnSelected); + } + + private void OnSelected(OnOrganSelected ev) + { + var entity = GetEntity(ev.Uid); + + if (TryComp(entity, out var partComponent) && partComponent.Body != null) + { + StartDrop(partComponent.Body.Value, entity); + } + else if (TryComp(entity, out var organComponent) && organComponent.Body != null) + { + StartDrop(organComponent.Body.Value, entity); + } + } + + private void OnUsing(EntityUid uid, BodyComponent component, InteractUsingEvent args) + { + if (args.Handled || !TryComp(args.Used, out _) || _mobState.IsAlive(uid) + || TryComp(uid, out _)) + return; + + if (!TryComp(uid, out _)) + return; + + var organs = GenList(uid); + + var ev = new OnSurgeryStarted(GetNetEntity(uid), organs); + RaiseNetworkEvent(ev, args.User); + } + + private OrganItem GetOrganItem(EntityUid part, List? child = null) + { + var metadata = MetaData(part); + + var organ = new OrganItem(metadata.EntityName, GetNetEntity(part), + new SpriteSpecifier.EntityPrototype(metadata.EntityPrototype!.ID)); + + if (child != null) + organ.Children = child; + + return organ; + } + + public List GenList(EntityUid uid) + { + var organs = new List(); + + if (TryComp(uid, out var bodyComponent)) + { + foreach (var (part, _) in _body.GetBodyChildren(uid, bodyComponent)) + { + if (part == uid) + { + continue; + } + + var child = GenList(part); + if (child.Count > 0) + organs.Add(GetOrganItem(part, child)); + } + } + else if (TryComp(uid, out var partComponent)) + { + foreach (var (part, _) in _body.GetBodyPartChildren(uid, partComponent)) + { + if (part == uid) + { + continue; + } + + var child = GenList(part); + if (child.Count > 0) + organs.Add(GetOrganItem(part, child)); + } + + foreach (var (part, _) in _body.GetPartOrgans(uid, partComponent)) + { + organs.Add(GetOrganItem(part, GenList(part))); + } + } + + return organs; + } + + public bool StartDrop(EntityUid uid, EntityUid organUid, EntityUid? user = null, BodyComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + EnsureComp(uid).OrganUid = organUid; + + var construct = EnsureComp(uid); + return _construction.ChangeGraph(uid, user, "BodySurgery", "head", true, construct); + } +} diff --git a/Content.Server/White/Construction/Completions/Surgery.cs b/Content.Server/White/Construction/Completions/Surgery.cs new file mode 100644 index 0000000000..6744954c27 --- /dev/null +++ b/Content.Server/White/Construction/Completions/Surgery.cs @@ -0,0 +1,28 @@ +using Content.Server.Body.Systems; +using Content.Shared.Body.Organ; +using Content.Shared.Construction; +using Content.Shared.White.CheapSurgery; + +namespace Content.Server.White.Construction.Completions; + +public sealed partial class Surgery : IGraphAction +{ + private ISawmill _sawmill = default!; + + public void PerformAction(EntityUid uid, EntityUid? userUid, IEntityManager entityManager) + { + _sawmill = Logger.GetSawmill("Surgery"); + var bodySystem = entityManager.EntitySysManager.GetEntitySystem(); + + if (!entityManager.TryGetComponent(uid, out var surgeryComponent)) + { + _sawmill.Warning($"Entity {uid} does not have a ActiveSurgery Component"); + return; + } + + if (entityManager.TryGetComponent(surgeryComponent.OrganUid, out var organComponent)) + bodySystem.RemoveOrgan(surgeryComponent.OrganUid, organComponent); + + entityManager.RemoveComponent(uid); + } +} diff --git a/Content.Shared/White/CheapSurgery/ActiveSurgeryComponent.cs b/Content.Shared/White/CheapSurgery/ActiveSurgeryComponent.cs new file mode 100644 index 0000000000..c1c5c1fa94 --- /dev/null +++ b/Content.Shared/White/CheapSurgery/ActiveSurgeryComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.White.CheapSurgery; + +[RegisterComponent] +public sealed partial class ActiveSurgeryComponent : Component +{ + [ViewVariables] public EntityUid OrganUid = EntityUid.Invalid; +} diff --git a/Content.Shared/White/CheapSurgery/OnSurgeryStarted.cs b/Content.Shared/White/CheapSurgery/OnSurgeryStarted.cs new file mode 100644 index 0000000000..28b843246c --- /dev/null +++ b/Content.Shared/White/CheapSurgery/OnSurgeryStarted.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.White.CheapSurgery; + +[Serializable, NetSerializable] +public sealed class OnSurgeryStarted : EntityEventArgs +{ + public NetEntity Target; + public List OrganItems; + + public OnSurgeryStarted(NetEntity target, List organItems) + { + Target = target; + OrganItems = organItems; + } +} + +[Serializable, NetSerializable] +public sealed class OrganItem +{ + public string Name; + public NetEntity Uid; + public SpriteSpecifier Icon; + public List Children = new(); + + public OrganItem(string name, NetEntity uid, SpriteSpecifier icon) + { + Name = name; + Uid = uid; + Icon = icon; + } +} + +[Serializable, NetSerializable] +public sealed class OnOrganSelected : EntityEventArgs +{ + public NetEntity Uid; + + public OnOrganSelected(NetEntity uid) + { + Uid = uid; + } +} diff --git a/Content.Shared/White/CheapSurgery/SharedCheapSurgerySystem.cs b/Content.Shared/White/CheapSurgery/SharedCheapSurgerySystem.cs new file mode 100644 index 0000000000..1a7e1dcd21 --- /dev/null +++ b/Content.Shared/White/CheapSurgery/SharedCheapSurgerySystem.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.White.CheapSurgery; + +public abstract class SharedCheapSurgerySystem : EntitySystem +{ + public override void Initialize() + { + + } +} diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index e157817ec4..5fc3a22941 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -54,7 +54,7 @@ - type: entity name: scalpel id: Scalpel - parent: BaseToolSurgery + parent: [BaseToolSurgery, BaseKnife] description: A surgical tool used to make incisions into flesh. components: - type: Sharp diff --git a/Resources/Prototypes/White/Construction/Surgery/body_surgery.yml b/Resources/Prototypes/White/Construction/Surgery/body_surgery.yml new file mode 100644 index 0000000000..a04d1dd542 --- /dev/null +++ b/Resources/Prototypes/White/Construction/Surgery/body_surgery.yml @@ -0,0 +1,21 @@ +- type: constructionGraph + id: BodySurgery + start: head + graph: + - node: head + edges: + - to: drop + steps: + - tool: Sawing + doAfter: 1 + + - tool: Slicing + doAfter: 1 + + - tool: Sawing + doAfter: 1 + + + - node: drop + actions: + - !type:Surgery