feat: простая хирургия

автор Грабля
This commit is contained in:
Remuchi
2024-01-26 16:08:08 +07:00
parent a1cb39c439
commit 895c546a99
9 changed files with 347 additions and 47 deletions

View File

@@ -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<OnSurgeryStarted>(OnStarted);
}
private void OnStarted(OnSurgeryStarted ev)
{
OpenRadialMenu(ev.OrganItems);
}
public void OpenRadialMenu(List<OrganItem> 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);
}
}

View File

@@ -6,6 +6,7 @@ using Content.Client.Viewport;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Animations; using Robust.Client.Animations;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
@@ -31,10 +32,10 @@ public sealed class RadialContainerCommandTest : LocalizedCommands
for (int i = 0; i < 24; i++) for (int i = 0; i < 24; i++)
{ {
var testButton = radial.AddButton("Action " + i, "/Textures/Interface/emotions.svg.192dpi.png"); 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"); Logger.Debug("Close event for your own logic");
}; };
@@ -46,17 +47,16 @@ public sealed class RadialContainerCommandTest : LocalizedCommands
[GenerateTypedNameReferences, Virtual] [GenerateTypedNameReferences, Virtual]
public partial class RadialContainer : Control public partial class RadialContainer : Control
{ {
private bool _isOpened = false; private bool _isOpened;
private Vector2 _focusSize = new Vector2(64, 64); private Vector2 _focusSize = new(64, 64);
private Vector2 _normalSize = new Vector2(50, 50); private Vector2 _normalSize = new(50, 50);
private float _moveAniTime = 0.3f; private IResourceCache _resourceCache;
private float _focusAniTime = 0.25f;
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 MoveAnimationKey = "move";
public const string InSizeAnimationKey = "insize"; public const string InSizeAnimationKey = "insize";
@@ -67,29 +67,24 @@ public partial class RadialContainer : Control
get => _focusSize.Y; get => _focusSize.Y;
set => _focusSize = new Vector2(value, value); set => _focusSize = new Vector2(value, value);
} }
public float NormalSize public float NormalSize
{ {
get => _normalSize.Y; get => _normalSize.Y;
set => _normalSize = new Vector2(value, value); set => _normalSize = new Vector2(value, value);
} }
public float MoveAnimationTime public float MoveAnimationTime { get; set; } = 0.3f;
{
get => _moveAniTime; public float FocusAnimationTime { get; set; } = 0.25f;
set => _moveAniTime = value;
}
public float FocusAnimationTime
{
get => _focusAniTime;
set => _focusAniTime = value;
}
public bool IsAction = true; public bool IsAction = true;
public RadialContainer() : base() public RadialContainer()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_resourceCache = IoCManager.Resolve<IResourceCache>();
} }
public void Open(Vector2 position) public void Open(Vector2 position)
@@ -103,9 +98,10 @@ public partial class RadialContainer : Control
{ {
AddToRoot(); AddToRoot();
if (Parent != null) if (Parent != null)
LayoutContainer.SetPosition(this, (Parent.Size/2) - (this.Size/2)); LayoutContainer.SetPosition(this, Parent.Size / 2 - Size / 2);
else else
LayoutContainer.SetPosition(this, (UserInterfaceManager.MainViewport.Size/2) - (this.Size/2)); LayoutContainer.SetPosition(this, UserInterfaceManager.MainViewport.Size / 2 - Size / 2);
UpdateButtons(); UpdateButtons();
} }
@@ -114,7 +110,7 @@ public partial class RadialContainer : Control
if (UserInterfaceManager.ActiveScreen == null) if (UserInterfaceManager.ActiveScreen == null)
return; return;
var ent = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity; var ent = IoCManager.Resolve<IPlayerManager>().LocalSession?.AttachedEntity;
if (ent == null) if (ent == null)
return; return;
@@ -122,7 +118,11 @@ public partial class RadialContainer : Control
return; return;
AddToRoot(); AddToRoot();
LayoutContainer.SetPosition(this, (IoCManager.Resolve<IEyeManager>().MapToScreen(xform.MapPosition).Position * 1.5f)); var position = IoCManager.Resolve<TransformSystem>().GetMapCoordinates(xform);
LayoutContainer.SetPosition(this,
IoCManager.Resolve<IEyeManager>().MapToScreen(position).Position * 1.5f);
UpdateButtons(); UpdateButtons();
} }
@@ -139,9 +139,21 @@ public partial class RadialContainer : Control
{ {
var button = new RadialButton(); var button = new RadialButton();
button.Content = action; button.Content = action;
button.Controller.TextureNormal = IoCManager.Resolve<IResourceCache>().GetTexture(_backgroundTexture); button.Controller.TextureNormal = _resourceCache.GetTexture(BackgroundTexture);
if (texture != null) if (texture != null)
button.BackgroundTexture.Texture = IoCManager.Resolve<IResourceCache>().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); Layout.AddChild(button);
return button; return button;
@@ -151,6 +163,7 @@ public partial class RadialContainer : Control
{ {
if (_isOpened) if (_isOpened)
return; return;
UserInterfaceManager.WindowRoot.AddChild(this); UserInterfaceManager.WindowRoot.AddChild(this);
_isOpened = !_isOpened; _isOpened = !_isOpened;
} }
@@ -159,31 +172,33 @@ public partial class RadialContainer : Control
{ {
Visible = true; Visible = true;
var angleDegrees = 360/Layout.ChildCount; var angleDegrees = 360 / Layout.ChildCount;
var stepAngle = -angleDegrees + -90; var stepAngle = -angleDegrees + -90;
var distance = FocusSize * 1.2; 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) foreach (var child in Layout.Children)
{ {
var button = (RadialButton)child; var button = (RadialButton) child;
button.ButtonSize = _normalSize; button.ButtonSize = _normalSize;
stepAngle += angleDegrees; stepAngle += angleDegrees;
var pos = GetPointFromPolar(stepAngle, distance); var pos = GetPointFromPolar(stepAngle, distance);
PlayRadialAnimation(button, pos, MoveAnimationKey); PlayRadialAnimation(button, pos, MoveAnimationKey);
button.Controller.OnMouseEntered += (_) => button.Controller.OnMouseEntered += _ =>
{ {
PlaySizeAnimation(button, _focusSize, OutSizeAnimationKey, InSizeAnimationKey); PlaySizeAnimation(button, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
ActionLabel.Text = button.Content ?? string.Empty; ActionLabel.Text = button.Content ?? string.Empty;
ActionLabel.Visible = IsAction; ActionLabel.Visible = IsAction;
}; };
button.Controller.OnMouseExited += (_) =>
button.Controller.OnMouseExited += _ =>
{ {
PlaySizeAnimation(button, _normalSize, InSizeAnimationKey, OutSizeAnimationKey); PlaySizeAnimation(button, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
ActionLabel.Visible = false; ActionLabel.Visible = false;
@@ -191,18 +206,20 @@ public partial class RadialContainer : Control
} }
CloseButton.ButtonSize = _normalSize; CloseButton.ButtonSize = _normalSize;
CloseButton.Controller.OnMouseEntered += (_) => CloseButton.Controller.OnMouseEntered += _ =>
{ {
PlaySizeAnimation(CloseButton, _focusSize, OutSizeAnimationKey, InSizeAnimationKey); PlaySizeAnimation(CloseButton, _focusSize, OutSizeAnimationKey, InSizeAnimationKey);
ActionLabel.Text = CloseButton.Content ?? string.Empty; ActionLabel.Text = CloseButton.Content ?? string.Empty;
ActionLabel.Visible = true; ActionLabel.Visible = true;
}; };
CloseButton.Controller.OnMouseExited += (_) =>
CloseButton.Controller.OnMouseExited += _ =>
{ {
PlaySizeAnimation(CloseButton, _normalSize, InSizeAnimationKey, OutSizeAnimationKey); PlaySizeAnimation(CloseButton, _normalSize, InSizeAnimationKey, OutSizeAnimationKey);
ActionLabel.Visible = false; ActionLabel.Visible = false;
}; };
CloseButton.Controller.OnPressed += (_) =>
CloseButton.Controller.OnPressed += _ =>
{ {
Close(); Close();
}; };
@@ -212,7 +229,7 @@ public partial class RadialContainer : Control
{ {
var anim = new Animation var anim = new Animation
{ {
Length = TimeSpan.FromMilliseconds(_moveAniTime * 1000), Length = TimeSpan.FromMilliseconds(MoveAnimationTime * 1000),
AnimationTracks = AnimationTracks =
{ {
new AnimationTrackControlProperty new AnimationTrackControlProperty
@@ -221,12 +238,13 @@ public partial class RadialContainer : Control
InterpolationMode = AnimationInterpolationMode.Linear, InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames = KeyFrames =
{ {
new AnimationTrackProperty.KeyFrame(new Vector2(0,0), 0f), new AnimationTrackProperty.KeyFrame(new Vector2(0, 0), 0f),
new AnimationTrackProperty.KeyFrame(pos, _moveAniTime) new AnimationTrackProperty.KeyFrame(pos, MoveAnimationTime)
} }
} }
} }
}; };
if (!button.HasRunningAnimation(playKey)) if (!button.HasRunningAnimation(playKey))
button.PlayAnimation(anim, playKey); button.PlayAnimation(anim, playKey);
} }
@@ -235,7 +253,7 @@ public partial class RadialContainer : Control
{ {
var anim = new Animation var anim = new Animation
{ {
Length = TimeSpan.FromMilliseconds(_focusAniTime * 1000), Length = TimeSpan.FromMilliseconds(FocusAnimationTime * 1000),
AnimationTracks = AnimationTracks =
{ {
new AnimationTrackControlProperty new AnimationTrackControlProperty
@@ -245,7 +263,7 @@ public partial class RadialContainer : Control
KeyFrames = KeyFrames =
{ {
new AnimationTrackProperty.KeyFrame(button.Size, 0f), 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)) if (stopKey != null && button.HasRunningAnimation(stopKey))
button.StopAnimation(stopKey); button.StopAnimation(stopKey);
if (!button.HasRunningAnimation(playKey)) if (!button.HasRunningAnimation(playKey))
button.PlayAnimation(anim, playKey); button.PlayAnimation(anim, playKey);
} }
@@ -263,11 +282,12 @@ public partial class RadialContainer : Control
foreach (var child in Layout.Children) foreach (var child in Layout.Children)
{ {
var button = (RadialButton)child; var button = (RadialButton) child;
LayoutContainer.SetPosition(child, button.Offset - (button.Size/2)); 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) private static Vector2 GetPointFromPolar(double angleDegrees, double distance)
@@ -277,6 +297,6 @@ public partial class RadialContainer : Control
var x = distance * Math.Cos(angleRadians); var x = distance * Math.Cos(angleRadians);
var y = distance * Math.Sin(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));
} }
} }

View File

@@ -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<BodyComponent, InteractUsingEvent>(OnUsing);
SubscribeNetworkEvent<OnOrganSelected>(OnSelected);
}
private void OnSelected(OnOrganSelected ev)
{
var entity = GetEntity(ev.Uid);
if (TryComp<BodyPartComponent>(entity, out var partComponent) && partComponent.Body != null)
{
StartDrop(partComponent.Body.Value, entity);
}
else if (TryComp<OrganComponent>(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<SharpComponent>(args.Used, out _) || _mobState.IsAlive(uid)
|| TryComp<ActiveSurgeryComponent>(uid, out _))
return;
if (!TryComp<HumanoidAppearanceComponent>(uid, out _))
return;
var organs = GenList(uid);
var ev = new OnSurgeryStarted(GetNetEntity(uid), organs);
RaiseNetworkEvent(ev, args.User);
}
private OrganItem GetOrganItem(EntityUid part, List<OrganItem>? 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<OrganItem> GenList(EntityUid uid)
{
var organs = new List<OrganItem>();
if (TryComp<BodyComponent>(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<BodyPartComponent>(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<ActiveSurgeryComponent>(uid).OrganUid = organUid;
var construct = EnsureComp<ConstructionComponent>(uid);
return _construction.ChangeGraph(uid, user, "BodySurgery", "head", true, construct);
}
}

View File

@@ -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<BodySystem>();
if (!entityManager.TryGetComponent<ActiveSurgeryComponent>(uid, out var surgeryComponent))
{
_sawmill.Warning($"Entity {uid} does not have a ActiveSurgery Component");
return;
}
if (entityManager.TryGetComponent<OrganComponent>(surgeryComponent.OrganUid, out var organComponent))
bodySystem.RemoveOrgan(surgeryComponent.OrganUid, organComponent);
entityManager.RemoveComponent<ActiveSurgeryComponent>(uid);
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Shared.White.CheapSurgery;
[RegisterComponent]
public sealed partial class ActiveSurgeryComponent : Component
{
[ViewVariables] public EntityUid OrganUid = EntityUid.Invalid;
}

View File

@@ -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<OrganItem> OrganItems;
public OnSurgeryStarted(NetEntity target, List<OrganItem> organItems)
{
Target = target;
OrganItems = organItems;
}
}
[Serializable, NetSerializable]
public sealed class OrganItem
{
public string Name;
public NetEntity Uid;
public SpriteSpecifier Icon;
public List<OrganItem> 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;
}
}

View File

@@ -0,0 +1,9 @@
namespace Content.Shared.White.CheapSurgery;
public abstract class SharedCheapSurgerySystem : EntitySystem
{
public override void Initialize()
{
}
}

View File

@@ -54,7 +54,7 @@
- type: entity - type: entity
name: scalpel name: scalpel
id: Scalpel id: Scalpel
parent: BaseToolSurgery parent: [BaseToolSurgery, BaseKnife]
description: A surgical tool used to make incisions into flesh. description: A surgical tool used to make incisions into flesh.
components: components:
- type: Sharp - type: Sharp

View File

@@ -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