Body System Part 1 POGGERS!!! (#855)
@@ -0,0 +1,53 @@
|
|||||||
|
using Content.Client.UserInterface;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Client.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Content.Client.BodySystem
|
||||||
|
{
|
||||||
|
public class BodyScannerBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
private BodyScannerDisplay _display;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private BodyScannerTemplateData _template;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private Dictionary<string, BodyScannerBodyPartData> _parts;
|
||||||
|
|
||||||
|
public BodyScannerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
_display = new BodyScannerDisplay(this);
|
||||||
|
_display.OnClose += Close;
|
||||||
|
_display.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
if (!(state is BodyScannerInterfaceState scannerState))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_template = scannerState.Template;
|
||||||
|
_parts = scannerState.Parts;
|
||||||
|
|
||||||
|
_display.UpdateDisplay(_template, _parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
using Content.Client.BodySystem;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
|
using Robust.Client.Interfaces.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.Utility;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using static Robust.Client.UserInterface.Controls.ItemList;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface
|
||||||
|
{
|
||||||
|
public sealed class BodyScannerDisplay : SS14Window
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly ILocalizationManager _loc;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
public BodyScannerBoundUserInterface Owner { get; private set; }
|
||||||
|
protected override Vector2? CustomSize => (800, 600);
|
||||||
|
private ItemList BodyPartList { get; }
|
||||||
|
private Label BodyPartLabel { get; }
|
||||||
|
private Label BodyPartHealth { get; }
|
||||||
|
private ItemList MechanismList { get; }
|
||||||
|
private RichTextLabel MechanismInfoLabel { get; }
|
||||||
|
|
||||||
|
|
||||||
|
private BodyScannerTemplateData _template;
|
||||||
|
private Dictionary<string, BodyScannerBodyPartData> _parts;
|
||||||
|
private List<string> _slots;
|
||||||
|
private BodyScannerBodyPartData _currentBodyPart;
|
||||||
|
|
||||||
|
|
||||||
|
public BodyScannerDisplay(BodyScannerBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
Owner = owner;
|
||||||
|
Title = _loc.GetString("Body Scanner");
|
||||||
|
|
||||||
|
var hSplit = new HBoxContainer();
|
||||||
|
Contents.AddChild(hSplit);
|
||||||
|
|
||||||
|
//Left half
|
||||||
|
var scrollBox = new ScrollContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
};
|
||||||
|
hSplit.AddChild(scrollBox);
|
||||||
|
BodyPartList = new ItemList { };
|
||||||
|
scrollBox.AddChild(BodyPartList);
|
||||||
|
BodyPartList.OnItemSelected += BodyPartOnItemSelected;
|
||||||
|
|
||||||
|
//Right half
|
||||||
|
var vSplit = new VBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
};
|
||||||
|
hSplit.AddChild(vSplit);
|
||||||
|
|
||||||
|
//Top half of the right half
|
||||||
|
var limbBox = new VBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
vSplit.AddChild(limbBox);
|
||||||
|
BodyPartLabel = new Label();
|
||||||
|
limbBox.AddChild(BodyPartLabel);
|
||||||
|
var limbHealthHBox = new HBoxContainer();
|
||||||
|
limbBox.AddChild(limbHealthHBox);
|
||||||
|
var healthLabel = new Label
|
||||||
|
{
|
||||||
|
Text = "Health: "
|
||||||
|
};
|
||||||
|
limbHealthHBox.AddChild(healthLabel);
|
||||||
|
BodyPartHealth = new Label();
|
||||||
|
limbHealthHBox.AddChild(BodyPartHealth);
|
||||||
|
var limbScroll = new ScrollContainer
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
limbBox.AddChild(limbScroll);
|
||||||
|
MechanismList = new ItemList();
|
||||||
|
limbScroll.AddChild(MechanismList);
|
||||||
|
MechanismList.OnItemSelected += MechanismOnItemSelected;
|
||||||
|
|
||||||
|
//Bottom half of the right half
|
||||||
|
MechanismInfoLabel = new RichTextLabel
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
vSplit.AddChild(MechanismInfoLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UpdateDisplay(BodyScannerTemplateData template, Dictionary<string, BodyScannerBodyPartData> parts)
|
||||||
|
{
|
||||||
|
_template = template;
|
||||||
|
_parts = parts;
|
||||||
|
_slots = new List<string>();
|
||||||
|
foreach (var (key, value) in _parts)
|
||||||
|
{
|
||||||
|
_slots.Add(key); //We have to do this since ItemLists only return the index of what item is selected and dictionaries don't allow you to explicitly grab things by index.
|
||||||
|
//So we put the contents of the dictionary into a list so that we can grab the list by index. I don't know either.
|
||||||
|
BodyPartList.AddItem(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void BodyPartOnItemSelected(ItemListSelectedEventArgs args)
|
||||||
|
{
|
||||||
|
if(_parts.TryGetValue(_slots[args.ItemIndex], out _currentBodyPart)) {
|
||||||
|
UpdateBodyPartBox(_currentBodyPart, _slots[args.ItemIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void UpdateBodyPartBox(BodyScannerBodyPartData part, string slotName)
|
||||||
|
{
|
||||||
|
BodyPartLabel.Text = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(slotName) + ": " + CultureInfo.CurrentCulture.TextInfo.ToTitleCase(part.Name);
|
||||||
|
BodyPartHealth.Text = part.CurrentDurability + "/" + part.MaxDurability;
|
||||||
|
|
||||||
|
MechanismList.Clear();
|
||||||
|
foreach (var mechanism in part.Mechanisms) {
|
||||||
|
MechanismList.AddItem(mechanism.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void MechanismOnItemSelected(ItemListSelectedEventArgs args)
|
||||||
|
{
|
||||||
|
UpdateMechanismBox(_currentBodyPart.Mechanisms[args.ItemIndex]);
|
||||||
|
}
|
||||||
|
private void UpdateMechanismBox(BodyScannerMechanismData mechanism)
|
||||||
|
{
|
||||||
|
//TODO: Make UI look less shit and clean up whatever the fuck this is lmao
|
||||||
|
if (mechanism != null)
|
||||||
|
{
|
||||||
|
string message = "";
|
||||||
|
message += mechanism.Name;
|
||||||
|
message += "\nHealth: ";
|
||||||
|
message += mechanism.CurrentDurability;
|
||||||
|
message += "/";
|
||||||
|
message += mechanism.MaxDurability;
|
||||||
|
message += "\n";
|
||||||
|
message += mechanism.Description;
|
||||||
|
MechanismInfoLabel.SetMessage(message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MechanismInfoLabel.SetMessage("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.GameObjects.Components.Storage;
|
||||||
|
using Content.Client.Interfaces.GameObjects;
|
||||||
|
using Robust.Client.Interfaces.GameObjects.Components;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Network;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Content.Client.BodySystem
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class ClientSurgeryToolComponent : SharedSurgeryToolComponent
|
||||||
|
{
|
||||||
|
private SurgeryToolWindow Window;
|
||||||
|
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
|
||||||
|
{
|
||||||
|
base.HandleNetworkMessage(message, channel, session);
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case OpenSurgeryUIMessage msg:
|
||||||
|
HandleOpenSurgeryUIMessage();
|
||||||
|
break;
|
||||||
|
case CloseSurgeryUIMessage msg:
|
||||||
|
HandleCloseSurgeryUIMessage();
|
||||||
|
break;
|
||||||
|
case UpdateSurgeryUIMessage msg:
|
||||||
|
HandleUpdateSurgeryUIMessage(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override void OnAdd()
|
||||||
|
{
|
||||||
|
base.OnAdd();
|
||||||
|
Window = new SurgeryToolWindow() { SurgeryToolEntity = this };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnRemove()
|
||||||
|
{
|
||||||
|
Window.Dispose();
|
||||||
|
base.OnRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleOpenSurgeryUIMessage()
|
||||||
|
{
|
||||||
|
Window.Open();
|
||||||
|
}
|
||||||
|
private void HandleCloseSurgeryUIMessage()
|
||||||
|
{
|
||||||
|
Window.Close();
|
||||||
|
}
|
||||||
|
private void HandleUpdateSurgeryUIMessage(UpdateSurgeryUIMessage surgeryUIState)
|
||||||
|
{
|
||||||
|
Window.BuildDisplay(surgeryUIState.Targets);
|
||||||
|
}
|
||||||
|
private class SurgeryToolWindow : SS14Window
|
||||||
|
{
|
||||||
|
private Control _VSplitContainer;
|
||||||
|
private VBoxContainer _bodyPartList;
|
||||||
|
public ClientSurgeryToolComponent SurgeryToolEntity;
|
||||||
|
|
||||||
|
protected override Vector2? CustomSize => (300, 400);
|
||||||
|
|
||||||
|
public SurgeryToolWindow()
|
||||||
|
{
|
||||||
|
Title = "Select surgery target...";
|
||||||
|
RectClipContent = true;
|
||||||
|
|
||||||
|
_VSplitContainer = new VBoxContainer();
|
||||||
|
var listScrollContainer = new ScrollContainer
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
HScrollEnabled = true,
|
||||||
|
VScrollEnabled = true
|
||||||
|
};
|
||||||
|
_bodyPartList = new VBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
listScrollContainer.AddChild(_bodyPartList);
|
||||||
|
_VSplitContainer.AddChild(listScrollContainer);
|
||||||
|
Contents.AddChild(_VSplitContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
SurgeryToolEntity.SendNetworkMessage(new CloseSurgeryUIMessage());
|
||||||
|
base.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildDisplay(Dictionary<string, string> targets)
|
||||||
|
{
|
||||||
|
_bodyPartList.DisposeAllChildren();
|
||||||
|
foreach (var(slotName, partname) in targets)
|
||||||
|
{
|
||||||
|
var button = new BodyPartButton(slotName);
|
||||||
|
button.ActualButton.OnToggled += OnButtonPressed;
|
||||||
|
button.LimbName.Text = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(slotName + " - " + partname);
|
||||||
|
|
||||||
|
//button.SpriteView.Sprite = sprite;
|
||||||
|
|
||||||
|
_bodyPartList.AddChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnButtonPressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
var parent = (BodyPartButton) args.Button.Parent;
|
||||||
|
SurgeryToolEntity.SendNetworkMessage(new SelectSurgeryUIMessage(parent.LimbSlotName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BodyPartButton : PanelContainer
|
||||||
|
{
|
||||||
|
public Button ActualButton { get; }
|
||||||
|
public SpriteView SpriteView { get; }
|
||||||
|
public Control EntityControl { get; }
|
||||||
|
public Label LimbName { get; }
|
||||||
|
public string LimbSlotName { get; }
|
||||||
|
|
||||||
|
public BodyPartButton(string slotName)
|
||||||
|
{
|
||||||
|
LimbSlotName = slotName;
|
||||||
|
ActualButton = new Button
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||||
|
ToggleMode = true,
|
||||||
|
MouseFilter = MouseFilterMode.Stop
|
||||||
|
};
|
||||||
|
AddChild(ActualButton);
|
||||||
|
|
||||||
|
var hBoxContainer = new HBoxContainer();
|
||||||
|
SpriteView = new SpriteView
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(32.0f, 32.0f)
|
||||||
|
};
|
||||||
|
LimbName = new Label
|
||||||
|
{
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
Text = "N/A",
|
||||||
|
};
|
||||||
|
hBoxContainer.AddChild(SpriteView);
|
||||||
|
hBoxContainer.AddChild(LimbName);
|
||||||
|
|
||||||
|
EntityControl = new Control
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
hBoxContainer.AddChild(EntityControl);
|
||||||
|
AddChild(hBoxContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
248
Content.Server/Health/BodySystem/BodyManagerComponent.cs
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component representing the many BodyParts attached to each other.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public class BodyManagerComponent : Component, IAttackHand {
|
||||||
|
|
||||||
|
public sealed override string Name => "BodyManager";
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
[Dependency]
|
||||||
|
private IPrototypeManager _prototypeManager;
|
||||||
|
#pragma warning restore
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private BodyTemplate _template;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private Dictionary<string, BodyPart> _partDictionary = new Dictionary<string, BodyPart>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The BodyTemplate that this BodyManagerComponent is adhering to.
|
||||||
|
/// </summary>
|
||||||
|
public BodyTemplate Template => _template;
|
||||||
|
/// <summary>
|
||||||
|
/// Maps BodyTemplate slot name to the BodyPart object filling it (if there is one).
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, BodyPart> PartDictionary => _partDictionary;
|
||||||
|
/// <summary>
|
||||||
|
/// List of all BodyParts in this body, taken from the keys of _parts.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<BodyPart> Parts
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _partDictionary.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recursive search that returns whether a given BodyPart is connected to the center BodyPart. Not efficient (O(n^2)), but most bodies don't have a ton of BodyParts.
|
||||||
|
/// </summary>
|
||||||
|
protected bool ConnectedToCenterPart(BodyPart target)
|
||||||
|
{
|
||||||
|
List<string> searchedSlots = new List<string> { };
|
||||||
|
if (TryGetSlotName(target, out string result))
|
||||||
|
return false;
|
||||||
|
return ConnectedToCenterPartRecursion(searchedSlots, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool ConnectedToCenterPartRecursion(List<string> searchedSlots, string slotName)
|
||||||
|
{
|
||||||
|
TryGetBodyPart(slotName, out BodyPart part);
|
||||||
|
if (part == GetCenterBodyPart())
|
||||||
|
return true;
|
||||||
|
searchedSlots.Add(slotName);
|
||||||
|
if (TryGetBodyPartConnections(slotName, out List<string> connections))
|
||||||
|
{
|
||||||
|
foreach (string connection in connections)
|
||||||
|
{
|
||||||
|
if (!searchedSlots.Contains(connection) && ConnectedToCenterPartRecursion(searchedSlots, connection))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the central BodyPart of this body based on the BodyTemplate. For humans, this is the torso. Returns null if not found.
|
||||||
|
/// </summary>
|
||||||
|
protected BodyPart GetCenterBodyPart()
|
||||||
|
{
|
||||||
|
_partDictionary.TryGetValue(_template.CenterSlot, out BodyPart center);
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grabs the BodyPart in the given slotName if there is one. Returns true if a BodyPart is found, false otherwise. If false, result will be null.
|
||||||
|
/// </summary>
|
||||||
|
protected bool TryGetBodyPart(string slotName, out BodyPart result)
|
||||||
|
{
|
||||||
|
return _partDictionary.TryGetValue(slotName, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grabs the slotName that the given BodyPart resides in. Returns true if the BodyPart is part of this body, false otherwise. If false, result will be null.
|
||||||
|
/// </summary>
|
||||||
|
protected bool TryGetSlotName(BodyPart part, out string result)
|
||||||
|
{
|
||||||
|
result = _partDictionary.FirstOrDefault(x => x.Value == part).Key; //We enforce that there is only one of each value in the dictionary, so we can iterate through the dictionary values to get the key from there.
|
||||||
|
return result == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grabs the names of all connected slots to the given slotName from the template. Returns true if connections are found to the slotName, false otherwise. If false, connections will be null.
|
||||||
|
/// </summary>
|
||||||
|
protected bool TryGetBodyPartConnections(string slotName, out List<string> connections)
|
||||||
|
{
|
||||||
|
return _template.Connections.TryGetValue(slotName, out connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////
|
||||||
|
///////// Server-specific stuff
|
||||||
|
/////////
|
||||||
|
|
||||||
|
public bool AttackHand(AttackHandEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
//TODO: remove organs?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer) {
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
|
||||||
|
string templateName = "";
|
||||||
|
serializer.DataField(ref templateName, "BaseTemplate", "bodyTemplate.Humanoid");
|
||||||
|
if (serializer.Reading)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex(templateName, out BodyTemplatePrototype templateData))
|
||||||
|
throw new InvalidOperationException("No BodyTemplatePrototype was found with the name " + templateName + " while loading a BodyTemplate!"); //Should never happen unless you fuck up the prototype.
|
||||||
|
|
||||||
|
string presetName = "";
|
||||||
|
serializer.DataField(ref presetName, "BasePreset", "bodyPreset.BasicHuman");
|
||||||
|
if (!_prototypeManager.TryIndex(presetName, out BodyPresetPrototype presetData))
|
||||||
|
throw new InvalidOperationException("No BodyPresetPrototype was found with the name " + presetName + " while loading a BodyPreset!"); //Should never happen unless you fuck up the prototype.
|
||||||
|
|
||||||
|
_template = new BodyTemplate(templateData);
|
||||||
|
LoadBodyPreset(new BodyPreset(presetData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the given preset - forcefully changes all limbs found in both the preset and this template!
|
||||||
|
/// </summary>
|
||||||
|
public void LoadBodyPreset(BodyPreset preset)
|
||||||
|
{
|
||||||
|
foreach (var (slotName, type) in _template.Slots)
|
||||||
|
{
|
||||||
|
if (!preset.PartIDs.TryGetValue(slotName, out string partID))
|
||||||
|
{ //For each slot in our BodyManagerComponent's template, try and grab what the ID of what the preset says should be inside it.
|
||||||
|
continue; //If the preset doesn't define anything for it, continue.
|
||||||
|
}
|
||||||
|
if (!_prototypeManager.TryIndex(partID, out BodyPartPrototype newPartData))
|
||||||
|
{ //Get the BodyPartPrototype corresponding to the BodyPart ID we grabbed.
|
||||||
|
throw new InvalidOperationException("BodyPart prototype with ID " + partID + " could not be found!");
|
||||||
|
}
|
||||||
|
_partDictionary.Remove(slotName); //Try and remove an existing limb if that exists.
|
||||||
|
_partDictionary.Add(slotName, new BodyPart(newPartData)); //Add a new BodyPart with the BodyPartPrototype as a baseline to our BodyComponent.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the current BodyTemplate to the new BodyTemplate. Attempts to keep previous BodyParts if there is a slot for them in both BodyTemplates.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeBodyTemplate(BodyTemplatePrototype newTemplate)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, BodyPart> part in _partDictionary)
|
||||||
|
{
|
||||||
|
//TODO: Make this work.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grabs all limbs of the given type in this body.
|
||||||
|
/// </summary>
|
||||||
|
public List<BodyPart> GetBodyPartsOfType(BodyPartType type)
|
||||||
|
{
|
||||||
|
List<BodyPart> toReturn = new List<BodyPart>();
|
||||||
|
foreach (var (slotName, bodyPart) in _partDictionary)
|
||||||
|
{
|
||||||
|
if (bodyPart.PartType == type)
|
||||||
|
toReturn.Add(bodyPart);
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects the given BodyPart reference, potentially dropping other BodyParts if they were hanging off it.
|
||||||
|
/// </summary>
|
||||||
|
public void DisconnectBodyPart(BodyPart part, bool dropEntity)
|
||||||
|
{
|
||||||
|
if (!_partDictionary.ContainsValue(part))
|
||||||
|
return;
|
||||||
|
if (part != null)
|
||||||
|
{
|
||||||
|
string slotName = _partDictionary.FirstOrDefault(x => x.Value == part).Key;
|
||||||
|
if (TryGetBodyPartConnections(slotName, out List<string> connections)) //Call disconnect on all limbs that were hanging off this limb.
|
||||||
|
{
|
||||||
|
foreach (string connectionName in connections) //This loop is an unoptimized travesty. TODO: optimize to be less shit
|
||||||
|
{
|
||||||
|
if (TryGetBodyPart(connectionName, out BodyPart result) && !ConnectedToCenterPart(result))
|
||||||
|
{
|
||||||
|
DisconnectBodyPartByName(connectionName, dropEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_partDictionary.Remove(slotName);
|
||||||
|
if (dropEntity)
|
||||||
|
{
|
||||||
|
var partEntity = Owner.EntityManager.SpawnEntity("BaseDroppedBodyPart", Owner.Transform.GridPosition);
|
||||||
|
partEntity.GetComponent<DroppedBodyPartComponent>().TransferBodyPartData(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal string version of DisconnectBodyPart for performance purposes.
|
||||||
|
/// </summary>
|
||||||
|
private void DisconnectBodyPartByName(string name, bool dropEntity)
|
||||||
|
{
|
||||||
|
if (!TryGetBodyPart(name, out BodyPart part))
|
||||||
|
return;
|
||||||
|
if (part != null)
|
||||||
|
{
|
||||||
|
if (TryGetBodyPartConnections(name, out List<string> connections))
|
||||||
|
{
|
||||||
|
foreach (string connectionName in connections)
|
||||||
|
{
|
||||||
|
if (TryGetBodyPart(connectionName, out BodyPart result) && !ConnectedToCenterPart(result))
|
||||||
|
{
|
||||||
|
DisconnectBodyPartByName(connectionName, dropEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_partDictionary.Remove(name);
|
||||||
|
if (dropEntity)
|
||||||
|
{
|
||||||
|
var partEntity = Owner.EntityManager.SpawnEntity("BaseDroppedBodyPart", Owner.Transform.GridPosition);
|
||||||
|
partEntity.GetComponent<DroppedBodyPartComponent>().TransferBodyPartData(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
226
Content.Server/Health/BodySystem/BodyPart/BodyPart.cs
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data class representing a singular limb such as an arm or a leg. Typically held within a BodyManagerComponent,
|
||||||
|
/// which coordinates functions between BodyParts.
|
||||||
|
/// </summary>
|
||||||
|
public class BodyPart
|
||||||
|
{
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private ISurgeryData _surgeryData;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private List<Mechanism> _mechanisms = new List<Mechanism>();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private int _sizeUsed = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Body part name.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plural version of this body part's name.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string Plural { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path to the RSI that represents this BodyPart.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state that represents this BodyPart.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIState { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BodyPartType that this body part is considered.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartType PartType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max HP of this body part.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int MaxDurability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current HP of this body part based on sum of all damage types.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int CurrentDurability => MaxDurability - CurrentDamages.Damage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current damage dealt to this BodyPart.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public AbstractDamageContainer CurrentDamages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// At what HP this body part is completely destroyed.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int DestroyThreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Armor of the body part against attacks.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public float Resistance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines many things: how many mechanisms can be fit inside a body part, fitting through tiny crevices, etc.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int Size { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What types of body parts this body part can attach to. For the most part, most limbs aren't universal and require extra work to attach between types.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartCompatibility Compatibility { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of IExposeData properties, allowing for additional data classes to be attached to a limb, such as a "length" class to an arm.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public List<IExposeData> Properties { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all Mechanisms currently inside this BodyPart.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public List<Mechanism> Mechanisms => _mechanisms;
|
||||||
|
|
||||||
|
public BodyPart(){}
|
||||||
|
|
||||||
|
public BodyPart(BodyPartPrototype data)
|
||||||
|
{
|
||||||
|
LoadFromPrototype(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to add a Mechanism. Returns true if successful, false if there was an error (e.g. not enough room in BodyPart). Use InstallDroppedMechanism if you want to easily install an IEntity with a DroppedMechanismComponent.
|
||||||
|
/// </summary>
|
||||||
|
public bool InstallMechanism(Mechanism mechanism)
|
||||||
|
{
|
||||||
|
if (_sizeUsed + mechanism.Size > Size)
|
||||||
|
return false; //No space
|
||||||
|
_mechanisms.Add(mechanism);
|
||||||
|
_sizeUsed += mechanism.Size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to install a DroppedMechanismComponent into the given limb, potentially deleting the dropped IEntity. Returns true if successful, false if there was an error (e.g. not enough room in BodyPart).
|
||||||
|
/// </summary>
|
||||||
|
public bool InstallDroppedMechanism(DroppedMechanismComponent droppedMechanism)
|
||||||
|
{
|
||||||
|
if (_sizeUsed + droppedMechanism.ContainedMechanism.Size > Size)
|
||||||
|
return false; //No space
|
||||||
|
InstallMechanism(droppedMechanism.ContainedMechanism);
|
||||||
|
droppedMechanism.Owner.Delete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to remove the given Mechanism reference from the given BodyPart reference. Returns null if there was an error in spawning the entity or removing the mechanism, otherwise returns a reference to the DroppedMechanismComponent on the newly spawned entity.
|
||||||
|
/// </summary>
|
||||||
|
public DroppedMechanismComponent DropMechanism(IEntity dropLocation, Mechanism mechanismTarget)
|
||||||
|
{
|
||||||
|
if (!_mechanisms.Contains(mechanismTarget))
|
||||||
|
return null;
|
||||||
|
_mechanisms.Remove(mechanismTarget);
|
||||||
|
_sizeUsed -= mechanismTarget.Size;
|
||||||
|
IEntityManager entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var mechanismEntity = entityManager.SpawnEntity("BaseDroppedMechanism", dropLocation.Transform.GridPosition);
|
||||||
|
var droppedMechanism = mechanismEntity.GetComponent<DroppedMechanismComponent>();
|
||||||
|
droppedMechanism.InitializeDroppedMechanism(mechanismTarget);
|
||||||
|
return droppedMechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to destroy the given Mechanism in the given BodyPart. Returns false if there was an error, true otherwise. Does NOT spawn a dropped entity.
|
||||||
|
/// </summary>
|
||||||
|
public bool DestroyMechanism(BodyPart bodyPartTarget, Mechanism mechanismTarget)
|
||||||
|
{
|
||||||
|
if (!_mechanisms.Contains(mechanismTarget))
|
||||||
|
return false;
|
||||||
|
_mechanisms.Remove(mechanismTarget);
|
||||||
|
_sizeUsed -= mechanismTarget.Size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given SurgertToolType can be used on the current state of this BodyPart (e.g.
|
||||||
|
/// </summary>
|
||||||
|
public bool SurgeryCheck(SurgeryToolType toolType)
|
||||||
|
{
|
||||||
|
return _surgeryData.CheckSurgery(toolType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to perform surgery on this BodyPart with the given tool. Returns false if there was an error, true if successful.
|
||||||
|
/// </summary>
|
||||||
|
public bool AttemptSurgery(SurgeryToolType toolType, BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
return _surgeryData.PerformSurgery(toolType, target, performer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the given BodyPartPrototype - current data on this BodyPart will be overwritten!
|
||||||
|
/// </summary>
|
||||||
|
public virtual void LoadFromPrototype(BodyPartPrototype data)
|
||||||
|
{
|
||||||
|
Name = data.Name;
|
||||||
|
Plural = data.Plural;
|
||||||
|
PartType = data.PartType;
|
||||||
|
RSIPath = data.RSIPath;
|
||||||
|
RSIState = data.RSIState;
|
||||||
|
MaxDurability = data.Durability;
|
||||||
|
CurrentDamages = new BiologicalDamageContainer();
|
||||||
|
Resistance = data.Resistance;
|
||||||
|
Size = data.Size;
|
||||||
|
Compatibility = data.Compatibility;
|
||||||
|
Properties = data.Properties;
|
||||||
|
//_surgeryData = (ISurgeryData) Activator.CreateInstance(null, data.SurgeryDataName);
|
||||||
|
//TODO: figure out a way to convert a string name in the YAML to the proper class (reflection won't work for reasons)
|
||||||
|
_surgeryData = new BiologicalSurgeryData(this);
|
||||||
|
IPrototypeManager prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
foreach (string mechanismPrototypeID in data.Mechanisms)
|
||||||
|
{
|
||||||
|
if (!prototypeManager.TryIndex(mechanismPrototypeID, out MechanismPrototype mechanismData))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No MechanismPrototype was found with the name " + mechanismPrototypeID + " while loading a BodyPartPrototype!");
|
||||||
|
}
|
||||||
|
_mechanisms.Add(new Mechanism(mechanismData));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System.Globalization;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component containing the data for a dropped BodyPart entity.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public class DroppedBodyPartComponent : Component {
|
||||||
|
|
||||||
|
public sealed override string Name => "DroppedBodyPart";
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private BodyPart _containedBodyPart;
|
||||||
|
|
||||||
|
public void TransferBodyPartData(BodyPart data)
|
||||||
|
{
|
||||||
|
_containedBodyPart = data;
|
||||||
|
Owner.Name = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(_containedBodyPart.Name);
|
||||||
|
if (Owner.TryGetComponent<SpriteComponent>(out SpriteComponent component))
|
||||||
|
{
|
||||||
|
component.LayerSetRSI(0, data.RSIPath);
|
||||||
|
component.LayerSetState(0, data.RSIState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Content.Server/Health/BodySystem/BodyPreset/BodyPreset.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores data on what BodyPart(Prototypes) should fill a BodyTemplate. Used for loading complete body presets, like a "basic human" with all human limbs.
|
||||||
|
/// </summary>
|
||||||
|
public class BodyPreset {
|
||||||
|
private string _name;
|
||||||
|
private Dictionary<string,string> _partIDs;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a template slot to the ID of the BodyPart that should fill it. E.g. "right arm" : "BodyPart.arm.basic_human".
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, string> PartIDs => _partIDs;
|
||||||
|
|
||||||
|
public BodyPreset(BodyPresetPrototype data)
|
||||||
|
{
|
||||||
|
LoadFromPrototype(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void LoadFromPrototype(BodyPresetPrototype data)
|
||||||
|
{
|
||||||
|
_name = data.Name;
|
||||||
|
_partIDs = data.PartIDs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(IActivate))]
|
||||||
|
public class BodyScannerComponent : Component, IActivate
|
||||||
|
{
|
||||||
|
public sealed override string Name => "BodyScanner";
|
||||||
|
|
||||||
|
private BoundUserInterface _userInterface;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(BodyScannerUiKey.Key);
|
||||||
|
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (actor.playerSession.AttachedEntity.TryGetComponent(out BodyManagerComponent attempt))
|
||||||
|
{
|
||||||
|
_userInterface.SetState(PrepareBodyScannerInterfaceState(attempt.Template, attempt.PartDictionary));
|
||||||
|
}
|
||||||
|
_userInterface.Open(actor.playerSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy BodyTemplate and BodyPart data into a common data class that the client can read.
|
||||||
|
/// </summary>
|
||||||
|
private BodyScannerInterfaceState PrepareBodyScannerInterfaceState(BodyTemplate template, Dictionary<string, BodyPart> bodyParts)
|
||||||
|
{
|
||||||
|
Dictionary<string, BodyScannerBodyPartData> partsData = new Dictionary<string, BodyScannerBodyPartData>();
|
||||||
|
foreach (var(slotname, bpart) in bodyParts) {
|
||||||
|
List<BodyScannerMechanismData> mechanismData = new List<BodyScannerMechanismData>();
|
||||||
|
foreach (var mech in bpart.Mechanisms)
|
||||||
|
{
|
||||||
|
mechanismData.Add(new BodyScannerMechanismData(mech.Name, mech.Description, mech.RSIPath, mech.RSIState, mech.MaxDurability, mech.CurrentDurability));
|
||||||
|
}
|
||||||
|
partsData.Add(slotname, new BodyScannerBodyPartData(bpart.Name, bpart.RSIPath, bpart.RSIState, bpart.MaxDurability, bpart.CurrentDurability, mechanismData));
|
||||||
|
}
|
||||||
|
BodyScannerTemplateData templateData = new BodyScannerTemplateData(template.Name, template.Slots);
|
||||||
|
return new BodyScannerInterfaceState(partsData, templateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class is a data capsule representing the standard format of a body. For instance, the "humanoid" BodyTemplate
|
||||||
|
/// defines two arms, each connected to a torso and so on. Capable of loading data from a BodyTemplatePrototype.
|
||||||
|
/// </summary>
|
||||||
|
public class BodyTemplate {
|
||||||
|
|
||||||
|
private int _hash;
|
||||||
|
private string _name;
|
||||||
|
private string _centerSlot;
|
||||||
|
private Dictionary<string, BodyPartType> _slots = new Dictionary<string, BodyPartType>();
|
||||||
|
private Dictionary<string, List<string>> _connections = new Dictionary<string, List<string>>();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Hash => _hash;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the center BodyPart. For humans, this is set to "torso". Used in many calculations.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string CenterSlot => _centerSlot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps all parts on this template to its BodyPartType. For instance, "right arm" is mapped to "BodyPartType.arm" on the humanoid template.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, BodyPartType> Slots => _slots;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps limb name to the list of their connections to other limbs. For instance, on the humanoid template "torso" is mapped to a list containing "right arm", "left arm",
|
||||||
|
/// "left leg", and "right leg". Only one of the limbs in a connection has to map it, i.e. humanoid template chooses to map "head" to "torso" and not the other way around.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, List<string>> Connections => _connections;
|
||||||
|
|
||||||
|
public BodyTemplate()
|
||||||
|
{
|
||||||
|
_name = "empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
public BodyTemplate(BodyTemplatePrototype data)
|
||||||
|
{
|
||||||
|
LoadFromPrototype(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Somewhat costly operation. Stores an integer unique to this exact BodyTemplate in _hash when called.
|
||||||
|
/// </summary>
|
||||||
|
private void CacheHashCode()
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
foreach (var(key, value) in _slots)
|
||||||
|
{
|
||||||
|
hash = HashCode.Combine<int, int>(hash, key.GetHashCode());
|
||||||
|
}
|
||||||
|
foreach (var (key, value) in _connections)
|
||||||
|
{
|
||||||
|
hash = HashCode.Combine<int, int>(hash, key.GetHashCode());
|
||||||
|
foreach (var connection in value)
|
||||||
|
{
|
||||||
|
hash = HashCode.Combine<int, int>(hash, connection.GetHashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void LoadFromPrototype(BodyTemplatePrototype data)
|
||||||
|
{
|
||||||
|
_name = data.Name;
|
||||||
|
_centerSlot = data.CenterSlot;
|
||||||
|
_slots = data.Slots;
|
||||||
|
_connections = data.Connections;
|
||||||
|
CacheHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System.Globalization;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component containing the data for a dropped Mechanism entity.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public class DroppedMechanismComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
[Dependency]
|
||||||
|
private IPrototypeManager _prototypeManager;
|
||||||
|
#pragma warning restore
|
||||||
|
|
||||||
|
public sealed override string Name => "DroppedMechanism";
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private Mechanism _containedMechanism;
|
||||||
|
|
||||||
|
public Mechanism ContainedMechanism => _containedMechanism;
|
||||||
|
|
||||||
|
public void InitializeDroppedMechanism(Mechanism data)
|
||||||
|
{
|
||||||
|
_containedMechanism = data;
|
||||||
|
Owner.Name = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(_containedMechanism.Name);
|
||||||
|
if (Owner.TryGetComponent<SpriteComponent>(out SpriteComponent component))
|
||||||
|
{
|
||||||
|
component.LayerSetRSI(0, data.RSIPath);
|
||||||
|
component.LayerSetState(0, data.RSIState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
106
Content.Server/Health/BodySystem/Mechanism/Mechanism.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data class representing a persistent item inside a BodyPart. This includes livers, eyes, cameras, brains, explosive implants, binary communicators, etc.
|
||||||
|
/// </summary>
|
||||||
|
public class Mechanism {
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Description shown in a mechanism installation console or when examining an uninstalled mechanism.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The message to display upon examining a mob with this mechanism installed. If the string is empty (""), no message will be displayed.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string ExamineMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path to the RSI that represents this Mechanism.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state that represents this Mechanism.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIState { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max HP of this mechanism.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int MaxDurability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current HP of this mechanism.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int CurrentDurability { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// At what HP this mechanism is completely destroyed.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int DestroyThreshold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Armor of this mechanism against attacks.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int Resistance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines a handful of things - mostly whether this mechanism can fit into a BodyPart.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int Size { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What kind of BodyParts this mechanism can be installed into.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartCompatibility Compatibility { get; set; }
|
||||||
|
|
||||||
|
public Mechanism(MechanismPrototype data)
|
||||||
|
{
|
||||||
|
LoadFromPrototype(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the given MechanismPrototype - current data on this Mechanism will be overwritten!
|
||||||
|
/// </summary>
|
||||||
|
public void LoadFromPrototype(MechanismPrototype data)
|
||||||
|
{
|
||||||
|
Name = data.Name;
|
||||||
|
Description = data.Description;
|
||||||
|
ExamineMessage = data.ExamineMessage;
|
||||||
|
RSIPath = data.RSIPath;
|
||||||
|
RSIState = data.RSIState;
|
||||||
|
MaxDurability = data.Durability;
|
||||||
|
CurrentDurability = MaxDurability;
|
||||||
|
DestroyThreshold = data.DestroyThreshold;
|
||||||
|
Resistance = data.Resistance;
|
||||||
|
Size = data.Size;
|
||||||
|
Compatibility = data.Compatibility;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.BodySystem;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.Components.Items;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Map;
|
||||||
|
using Robust.Shared.Interfaces.Network;
|
||||||
|
using Robust.Shared.Interfaces.Physics;
|
||||||
|
using Robust.Shared.Interfaces.Timing;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Log;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Weapon.Melee
|
||||||
|
{
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public class ServerSurgeryToolComponent : SharedSurgeryToolComponent, IAfterAttack
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IMapManager _mapManager;
|
||||||
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||||
|
[Dependency] private readonly IPhysicsManager _physicsManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
public HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>();
|
||||||
|
private Dictionary<string, BodyPart> _surgeryOptionsCache = new Dictionary<string, BodyPart>();
|
||||||
|
private BodyManagerComponent _targetCache;
|
||||||
|
private IEntity _performerCache;
|
||||||
|
|
||||||
|
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (eventArgs.Attacked == null)
|
||||||
|
return;
|
||||||
|
if (eventArgs.Attacked.TryGetComponent<BodySystem.BodyManagerComponent>(out BodySystem.BodyManagerComponent bodyManager))
|
||||||
|
{
|
||||||
|
_surgeryOptionsCache.Clear();
|
||||||
|
var toSend = new Dictionary<string, string>();
|
||||||
|
foreach (var(key, value) in bodyManager.PartDictionary) {
|
||||||
|
if (value.SurgeryCheck(_surgeryToolClass))
|
||||||
|
{
|
||||||
|
_surgeryOptionsCache.Add(key, value);
|
||||||
|
toSend.Add(key, value.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_surgeryOptionsCache.Count > 0)
|
||||||
|
{
|
||||||
|
OpenSurgeryUI(eventArgs.User);
|
||||||
|
UpdateSurgeryUI(eventArgs.User, toSend);
|
||||||
|
_performerCache = eventArgs.User;
|
||||||
|
_targetCache = bodyManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after the user selects a surgery target.
|
||||||
|
/// </summary>
|
||||||
|
void PerformSurgery(SelectSurgeryUIMessage msg)
|
||||||
|
{
|
||||||
|
//TODO: sanity checks to see whether user is in range, body is still same, etc etc
|
||||||
|
if (!_surgeryOptionsCache.TryGetValue(msg.TargetSlot, out BodyPart target))
|
||||||
|
{
|
||||||
|
Logger.Debug("Error when trying to perform surgery on bodypart in slot " + msg.TargetSlot + ": it was not found!");
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
if (!target.AttemptSurgery(_surgeryToolClass, _targetCache, _performerCache))
|
||||||
|
{
|
||||||
|
Logger.Debug("Error when trying to perform surgery on bodypart " + target.Name + "!");
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
CloseSurgeryUI(_performerCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OpenSurgeryUI(IEntity character)
|
||||||
|
{
|
||||||
|
var user_session = character.GetComponent<BasicActorComponent>().playerSession;
|
||||||
|
SubscribeSession(user_session);
|
||||||
|
SendNetworkMessage(new OpenSurgeryUIMessage(), user_session.ConnectedClient);
|
||||||
|
}
|
||||||
|
public void UpdateSurgeryUI(IEntity character, Dictionary<string, string> options)
|
||||||
|
{
|
||||||
|
var user_session = character.GetComponent<BasicActorComponent>().playerSession;
|
||||||
|
if (user_session.AttachedEntity == null)
|
||||||
|
{
|
||||||
|
UnsubscribeSession(user_session);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SendNetworkMessage(new UpdateSurgeryUIMessage(options), user_session.ConnectedClient);
|
||||||
|
}
|
||||||
|
public void CloseSurgeryUI(IEntity character)
|
||||||
|
{
|
||||||
|
var user_session = character.GetComponent<BasicActorComponent>().playerSession;
|
||||||
|
SubscribeSession(user_session);
|
||||||
|
SendNetworkMessage(new CloseSurgeryUIMessage(), user_session.ConnectedClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
|
||||||
|
{
|
||||||
|
base.HandleNetworkMessage(message, channel, session);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(session));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case CloseSurgeryUIMessage msg:
|
||||||
|
UnsubscribeSession(session as IPlayerSession);
|
||||||
|
break;
|
||||||
|
case SelectSurgeryUIMessage msg:
|
||||||
|
PerformSurgery(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SubscribeSession(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (!SubscribedSessions.Contains(session))
|
||||||
|
{
|
||||||
|
session.PlayerStatusChanged += HandlePlayerSessionChangeEvent;
|
||||||
|
SubscribedSessions.Add(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void UnsubscribeSession(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (SubscribedSessions.Contains(session))
|
||||||
|
{
|
||||||
|
SubscribedSessions.Remove(session);
|
||||||
|
SendNetworkMessage(new CloseSurgeryUIMessage(), session.ConnectedClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void HandlePlayerSessionChangeEvent(object obj, SessionStatusEventArgs SSEA)
|
||||||
|
{
|
||||||
|
if (SSEA.NewStatus != SessionStatus.InGame)
|
||||||
|
{
|
||||||
|
UnsubscribeSession(SSEA.Session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data class representing the surgery state of a biological entity.
|
||||||
|
/// </summary>
|
||||||
|
public class BiologicalSurgeryData : ISurgeryData {
|
||||||
|
|
||||||
|
protected bool _skinOpened = false;
|
||||||
|
protected bool _vesselsClamped = false;
|
||||||
|
protected bool _skinRetracted = false;
|
||||||
|
protected Mechanism _targetOrgan;
|
||||||
|
|
||||||
|
public BiologicalSurgeryData(BodyPart parent) : base(parent) { }
|
||||||
|
|
||||||
|
public override SurgeryAction GetSurgeryStep(SurgeryToolType toolType)
|
||||||
|
{
|
||||||
|
if (_skinOpened)
|
||||||
|
{
|
||||||
|
if (_vesselsClamped)
|
||||||
|
{
|
||||||
|
if (_skinRetracted)
|
||||||
|
{
|
||||||
|
if (_targetOrgan != null && toolType == SurgeryToolType.VesselCompression)
|
||||||
|
return RemoveOrganSurgery;
|
||||||
|
if (toolType == SurgeryToolType.Incision) //_targetOrgan is potentially given a value by DisconnectOrganSurgery.
|
||||||
|
return DisconnectOrganSurgery;
|
||||||
|
else if (toolType == SurgeryToolType.Cauterization)
|
||||||
|
return CautizerizeIncisionSurgery;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (toolType == SurgeryToolType.Retraction)
|
||||||
|
return RetractSkinSurgery;
|
||||||
|
else if (toolType == SurgeryToolType.Cauterization)
|
||||||
|
return CautizerizeIncisionSurgery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (toolType == SurgeryToolType.VesselCompression)
|
||||||
|
return ClampVesselsSurgery;
|
||||||
|
else if (toolType == SurgeryToolType.Cauterization)
|
||||||
|
return CautizerizeIncisionSurgery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (toolType == SurgeryToolType.Incision)
|
||||||
|
return OpenSkinSurgery;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OpenSkinSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Cut open the skin..."));
|
||||||
|
//Delay?
|
||||||
|
_skinOpened = true;
|
||||||
|
}
|
||||||
|
protected void ClampVesselsSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Clamp the vessels..."));
|
||||||
|
//Delay?
|
||||||
|
_vesselsClamped = true;
|
||||||
|
}
|
||||||
|
protected void RetractSkinSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Retract the skin..."));
|
||||||
|
//Delay?
|
||||||
|
_skinRetracted = true;
|
||||||
|
}
|
||||||
|
protected void CautizerizeIncisionSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Cauterize the incision..."));
|
||||||
|
//Delay?
|
||||||
|
_skinOpened = false;
|
||||||
|
_vesselsClamped = false;
|
||||||
|
_skinRetracted = false;
|
||||||
|
}
|
||||||
|
protected void DisconnectOrganSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
Mechanism mechanismTarget = null;
|
||||||
|
//TODO: figureout popup, right now it just takes the first organ available if there is one
|
||||||
|
if (_parent.Mechanisms.Count > 0)
|
||||||
|
mechanismTarget = _parent.Mechanisms[0];
|
||||||
|
if (mechanismTarget != null)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Detach the organ..."));
|
||||||
|
//Delay?
|
||||||
|
_targetOrgan = mechanismTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
protected void RemoveOrganSurgery(BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
if (_targetOrgan != null)
|
||||||
|
{
|
||||||
|
ILocalizationManager localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||||
|
performer.PopupMessage(performer, localizationManager.GetString("Remove the organ..."));
|
||||||
|
//Delay?
|
||||||
|
_parent.DropMechanism(performer, _targetOrgan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.BodySystem;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Server.BodySystem {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This data class represents the state of a BodyPart in regards to everything surgery related - whether there's an incision on it, whether the bone is broken, etc.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ISurgeryData {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The BodyPart this surgeryData is attached to. The ISurgeryData class should not exist without a BodyPart that it represents, and will not work correctly without it.
|
||||||
|
/// </summary>
|
||||||
|
protected BodyPart _parent;
|
||||||
|
/// <summary>
|
||||||
|
/// The BodyPartType of the parent PartType.
|
||||||
|
/// </summary>
|
||||||
|
protected BodyPartType _parentType => _parent.PartType;
|
||||||
|
public delegate void SurgeryAction(BodyManagerComponent target, IEntity performer);
|
||||||
|
|
||||||
|
public ISurgeryData(BodyPart parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the delegate corresponding to the surgery step using the given SurgeryToolType. Returns null if no surgery step can be performed.
|
||||||
|
/// </summary>
|
||||||
|
public abstract SurgeryAction GetSurgeryStep(SurgeryToolType toolType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given SurgeryToolType can be used to perform a surgery.
|
||||||
|
/// </summary>
|
||||||
|
public bool CheckSurgery(SurgeryToolType toolType)
|
||||||
|
{
|
||||||
|
return GetSurgeryStep(toolType) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to perform surgery with the given tooltype. Returns whether the operation was successful.
|
||||||
|
/// </summary>
|
||||||
|
/// /// <param name="toolType">The SurgeryToolType used for this surgery.</param>
|
||||||
|
/// /// <param name="performer">The entity performing the surgery.</param>
|
||||||
|
public bool PerformSurgery(SurgeryToolType toolType, BodyManagerComponent target, IEntity performer)
|
||||||
|
{
|
||||||
|
SurgeryAction step = GetSurgeryStep(toolType);
|
||||||
|
if (step == null)
|
||||||
|
return false;
|
||||||
|
step(target, performer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,5 +44,7 @@
|
|||||||
public const uint GHOST = 1039;
|
public const uint GHOST = 1039;
|
||||||
public const uint MICROWAVE = 1040;
|
public const uint MICROWAVE = 1040;
|
||||||
public const uint GRAVITY_GENERATOR = 1041;
|
public const uint GRAVITY_GENERATOR = 1041;
|
||||||
|
public const uint SURGERY = 1042;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
class ArmLength : IExposeData {
|
||||||
|
private float _length;
|
||||||
|
|
||||||
|
public void ExposeData(ObjectSerializer serializer){
|
||||||
|
serializer.DataField(ref _length, "length", 2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype for the BodyPart class.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("bodyPart")]
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyPartPrototype : IPrototype, IIndexedPrototype {
|
||||||
|
private string _id;
|
||||||
|
private string _name;
|
||||||
|
private string _plural;
|
||||||
|
private string _rsiPath;
|
||||||
|
private string _rsiState;
|
||||||
|
private BodyPartType _partType;
|
||||||
|
private int _durability;
|
||||||
|
private int _destroyThreshold;
|
||||||
|
private float _resistance;
|
||||||
|
private int _size;
|
||||||
|
private BodyPartCompatibility _compatibility;
|
||||||
|
private string _surgeryDataName;
|
||||||
|
private List<IExposeData> _properties;
|
||||||
|
private List<string> _mechanisms;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ID => _id;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Plural => _plural;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIPath => _rsiPath;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIState => _rsiState;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartType PartType => _partType;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Durability => _durability;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int DestroyThreshold => _destroyThreshold;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public float Resistance => _resistance;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Size => _size;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartCompatibility Compatibility => _compatibility;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string SurgeryDataName => _surgeryDataName;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public List<IExposeData> Properties => _properties;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public List<string> Mechanisms => _mechanisms;
|
||||||
|
|
||||||
|
public virtual void LoadFrom(YamlMappingNode mapping){
|
||||||
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
|
|
||||||
|
serializer.DataField(ref _name, "name", string.Empty);
|
||||||
|
serializer.DataField(ref _id, "id", string.Empty);
|
||||||
|
serializer.DataField(ref _plural, "plural", string.Empty);
|
||||||
|
serializer.DataField(ref _rsiPath, "rsiPath", string.Empty);
|
||||||
|
serializer.DataField(ref _rsiState, "rsiState", string.Empty);
|
||||||
|
serializer.DataField(ref _partType, "partType", BodyPartType.Other);
|
||||||
|
serializer.DataField(ref _surgeryDataName, "surgeryDataType", "BiologicalSurgeryData");
|
||||||
|
serializer.DataField(ref _durability, "durability", 50);
|
||||||
|
serializer.DataField(ref _destroyThreshold, "destroyThreshold", -50);
|
||||||
|
serializer.DataField(ref _resistance, "resistance", 0f);
|
||||||
|
serializer.DataField(ref _size, "size", 0);
|
||||||
|
serializer.DataField(ref _compatibility, "compatibility", BodyPartCompatibility.Universal);
|
||||||
|
serializer.DataField(ref _properties, "properties", new List<IExposeData>());
|
||||||
|
serializer.DataField(ref _mechanisms, "mechanisms", new List<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype for the BodyPreset class.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("bodyPreset")]
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyPresetPrototype : IPrototype, IIndexedPrototype {
|
||||||
|
private string _id;
|
||||||
|
private string _name;
|
||||||
|
private Dictionary<string,string> _partIDs;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ID => _id;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, string> PartIDs => _partIDs;
|
||||||
|
|
||||||
|
public virtual void LoadFrom(YamlMappingNode mapping){
|
||||||
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
|
serializer.DataField(ref _id, "id", string.Empty);
|
||||||
|
serializer.DataField(ref _name, "name", string.Empty);
|
||||||
|
serializer.DataField(ref _partIDs, "partIDs", new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public enum BodyScannerUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyScannerInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public readonly Dictionary<string, BodyScannerBodyPartData> Parts;
|
||||||
|
public readonly BodyScannerTemplateData Template;
|
||||||
|
public BodyScannerInterfaceState(Dictionary<string, BodyScannerBodyPartData> parts, BodyScannerTemplateData template)
|
||||||
|
{
|
||||||
|
Template = template;
|
||||||
|
Parts = parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyScannerBodyPartData
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly string RSIPath;
|
||||||
|
public readonly string RSIState;
|
||||||
|
public readonly int MaxDurability;
|
||||||
|
public readonly int CurrentDurability;
|
||||||
|
public readonly List<BodyScannerMechanismData> Mechanisms;
|
||||||
|
public BodyScannerBodyPartData(string name, string rsiPath, string rsiState, int maxDurability, int currentDurability, List<BodyScannerMechanismData> mechanisms)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
RSIPath = rsiPath;
|
||||||
|
RSIState = rsiState;
|
||||||
|
MaxDurability = maxDurability;
|
||||||
|
CurrentDurability = currentDurability;
|
||||||
|
Mechanisms = mechanisms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyScannerMechanismData
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly string Description;
|
||||||
|
public readonly string RSIPath;
|
||||||
|
public readonly string RSIState;
|
||||||
|
public readonly int MaxDurability;
|
||||||
|
public readonly int CurrentDurability;
|
||||||
|
public BodyScannerMechanismData(string name, string description, string rsiPath, string rsiState, int maxDurability, int currentDurability)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Description = description;
|
||||||
|
RSIPath = rsiPath;
|
||||||
|
RSIState = rsiState;
|
||||||
|
MaxDurability = maxDurability;
|
||||||
|
CurrentDurability = currentDurability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyScannerTemplateData
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly Dictionary<string, BodyPartType> Slots;
|
||||||
|
public BodyScannerTemplateData(string name, Dictionary<string, BodyPartType> slots)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Slots = slots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype for the BodyTemplate class.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("bodyTemplate")]
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BodyTemplatePrototype : IPrototype, IIndexedPrototype {
|
||||||
|
private string _id;
|
||||||
|
private string _name;
|
||||||
|
private string _centerSlot;
|
||||||
|
private Dictionary<string, BodyPartType> _slots;
|
||||||
|
private Dictionary<string, List<string>> _connections;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ID => _id;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string CenterSlot => _centerSlot;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, BodyPartType> Slots => _slots;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<string, List<string>> Connections => _connections;
|
||||||
|
|
||||||
|
public virtual void LoadFrom(YamlMappingNode mapping){
|
||||||
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
|
serializer.DataField(ref _name, "name", string.Empty);
|
||||||
|
serializer.DataField(ref _id, "id", string.Empty);
|
||||||
|
serializer.DataField(ref _centerSlot, "centerSlot", string.Empty);
|
||||||
|
serializer.DataField(ref _slots, "slots", new Dictionary<string, BodyPartType>());
|
||||||
|
serializer.DataField(ref _connections, "connections", new Dictionary<string, List<string>>());
|
||||||
|
|
||||||
|
|
||||||
|
//Our prototypes don't force the user to define a BodyPart connection twice. E.g. Head: Torso v.s. Torso: Head.
|
||||||
|
//The user only has to do one. We want it to be that way in the code, though, so this cleans that up.
|
||||||
|
Dictionary<string, List<string>> cleanedConnections = new Dictionary<string, List<string>>();
|
||||||
|
foreach (var (targetSlotName, slotType) in _slots)
|
||||||
|
{
|
||||||
|
List<string> tempConnections = new List<string>();
|
||||||
|
foreach (var (slotName, slotConnections) in _connections)
|
||||||
|
{
|
||||||
|
if (slotName == targetSlotName){
|
||||||
|
foreach (string connection in slotConnections) {
|
||||||
|
if (!tempConnections.Contains(connection))
|
||||||
|
tempConnections.Add(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (slotConnections.Contains(slotName))
|
||||||
|
{
|
||||||
|
tempConnections.Add(slotName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tempConnections.Count > 0)
|
||||||
|
cleanedConnections.Add(targetSlotName, tempConnections);
|
||||||
|
}
|
||||||
|
_connections = cleanedConnections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Content.Shared/Health/BodySystem/BodysystemValues.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
namespace Content.Shared.BodySystem
|
||||||
|
{
|
||||||
|
public enum BodyPartCompatibility { Mechanical, Biological, Universal };
|
||||||
|
public enum BodyPartType { Other, Torso, Head, Arm, Hand, Leg, Foot };
|
||||||
|
public enum SurgeryToolType { Incision, Retraction, Cauterization, VesselCompression, Drilling, BoneSawing, Amputation }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype for the Mechanism class.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("mechanism")]
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class MechanismPrototype : IPrototype, IIndexedPrototype {
|
||||||
|
private string _id;
|
||||||
|
private string _name;
|
||||||
|
private string _description;
|
||||||
|
private string _examineMessage;
|
||||||
|
private string _spritePath;
|
||||||
|
private string _rsiPath;
|
||||||
|
private string _rsiState;
|
||||||
|
private int _durability;
|
||||||
|
private int _destroyThreshold;
|
||||||
|
private int _resistance;
|
||||||
|
private int _size;
|
||||||
|
private BodyPartCompatibility _compatibility;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ID => _id;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Name => _name;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string Description => _description;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ExamineMessage => _examineMessage;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIPath => _rsiPath;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string RSIState => _rsiState;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Durability => _durability;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int DestroyThreshold => _destroyThreshold;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Resistance => _resistance;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public int Size => _size;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public BodyPartCompatibility Compatibility => _compatibility;
|
||||||
|
|
||||||
|
public virtual void LoadFrom(YamlMappingNode mapping){
|
||||||
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
|
|
||||||
|
serializer.DataField(ref _id, "id", string.Empty);
|
||||||
|
serializer.DataField(ref _name, "name", string.Empty);
|
||||||
|
serializer.DataField(ref _description, "description", string.Empty);
|
||||||
|
serializer.DataField(ref _examineMessage, "examineMessage", string.Empty);
|
||||||
|
serializer.DataField(ref _rsiPath, "rsiPath", string.Empty);
|
||||||
|
serializer.DataField(ref _rsiState, "rsiState", string.Empty);
|
||||||
|
serializer.DataField(ref _durability, "durability", 0);
|
||||||
|
serializer.DataField(ref _destroyThreshold, "destroyThreshold", 0);
|
||||||
|
serializer.DataField(ref _resistance, "resistance", 0);
|
||||||
|
serializer.DataField(ref _size, "size", 2);
|
||||||
|
serializer.DataField(ref _compatibility, "compatibility", BodyPartCompatibility.Universal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem {
|
||||||
|
|
||||||
|
|
||||||
|
public class SharedSurgeryToolComponent : Component {
|
||||||
|
|
||||||
|
protected SurgeryToolType _surgeryToolClass;
|
||||||
|
protected float _baseOperateTime;
|
||||||
|
public override string Name => "SurgeryTool";
|
||||||
|
public override uint? NetID => ContentNetIDs.SURGERY;
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
serializer.DataField(ref _surgeryToolClass, "surgeryToolClass", SurgeryToolType.Incision);
|
||||||
|
serializer.DataField(ref _baseOperateTime, "baseOperateTime", 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class OpenSurgeryUIMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public OpenSurgeryUIMessage()
|
||||||
|
{
|
||||||
|
Directed = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class CloseSurgeryUIMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public CloseSurgeryUIMessage()
|
||||||
|
{
|
||||||
|
Directed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class UpdateSurgeryUIMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public Dictionary<string, string> Targets;
|
||||||
|
public UpdateSurgeryUIMessage(Dictionary<string, string> targets)
|
||||||
|
{
|
||||||
|
Targets = targets;
|
||||||
|
Directed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class SelectSurgeryUIMessage : ComponentMessage
|
||||||
|
{
|
||||||
|
public string TargetSlot;
|
||||||
|
public SelectSurgeryUIMessage(string target)
|
||||||
|
{
|
||||||
|
TargetSlot = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
107
Content.Shared/Health/DamageContainer/AbstractDamageContainer.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem
|
||||||
|
{
|
||||||
|
public enum DamageClass { Brute, Burn, Toxin, Airloss }
|
||||||
|
public enum DamageType { Blunt, Piercing, Heat, Disintegration, Cellular, DNA, Airloss }
|
||||||
|
|
||||||
|
public static class DamageContainerValues
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<DamageClass, List<DamageType>> DamageClassToType = new Dictionary<DamageClass, List<DamageType>>
|
||||||
|
{
|
||||||
|
{ DamageClass.Brute, new List<DamageType>{ DamageType.Blunt, DamageType.Piercing }},
|
||||||
|
{ DamageClass.Burn, new List<DamageType>{ DamageType.Heat, DamageType.Disintegration }},
|
||||||
|
{ DamageClass.Toxin, new List<DamageType>{ DamageType.Cellular, DamageType.DNA}},
|
||||||
|
{ DamageClass.Airloss, new List<DamageType>{ DamageType.Airloss }}
|
||||||
|
};
|
||||||
|
public static readonly Dictionary<DamageType, DamageClass> DamageTypeToClass = new Dictionary<DamageType, DamageClass>
|
||||||
|
{
|
||||||
|
{ DamageType.Blunt, DamageClass.Brute },
|
||||||
|
{ DamageType.Piercing, DamageClass.Brute },
|
||||||
|
{ DamageType.Heat, DamageClass.Burn },
|
||||||
|
{ DamageType.Disintegration, DamageClass.Burn },
|
||||||
|
{ DamageType.Cellular, DamageClass.Toxin },
|
||||||
|
{ DamageType.DNA, DamageClass.Toxin },
|
||||||
|
{ DamageType.Airloss, DamageClass.Airloss }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Abstract class for all damage container classes.
|
||||||
|
/// </summary>
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public abstract class AbstractDamageContainer
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
abstract public List<DamageClass> SupportedDamageClasses { get; }
|
||||||
|
|
||||||
|
private Dictionary<DamageType, int> _damageList = new Dictionary<DamageType, int>();
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<DamageType, int> DamageList => _damageList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sum of all damages kept on record.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int Damage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _damageList.Sum(x => x.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractDamageContainer()
|
||||||
|
{
|
||||||
|
foreach(DamageClass damageClass in SupportedDamageClasses){
|
||||||
|
DamageContainerValues.DamageClassToType.TryGetValue(damageClass, out List<DamageType> childrenDamageTypes);
|
||||||
|
foreach (DamageType damageType in childrenDamageTypes)
|
||||||
|
{
|
||||||
|
_damageList.Add(damageType, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to grab the damage value for the given DamageType - returns false if the container does not support that type.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryGetDamageValue(DamageType type, out int damage)
|
||||||
|
{
|
||||||
|
return _damageList.TryGetValue(type, out damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to set the damage value for the given DamageType - returns false if the container does not support that type.
|
||||||
|
/// </summary>
|
||||||
|
public bool TrySetDamageValue(DamageType type, int target)
|
||||||
|
{
|
||||||
|
DamageContainerValues.DamageTypeToClass.TryGetValue(type, out DamageClass classType);
|
||||||
|
if (SupportedDamageClasses.Contains(classType))
|
||||||
|
{
|
||||||
|
_damageList[type] = target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to change the damage value for the given DamageType - returns false if the container does not support that type.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryChangeDamageValue(DamageType type, int delta)
|
||||||
|
{
|
||||||
|
DamageContainerValues.DamageTypeToClass.TryGetValue(type, out DamageClass classType);
|
||||||
|
if (SupportedDamageClasses.Contains(classType))
|
||||||
|
{
|
||||||
|
_damageList[type] = _damageList[type] + delta;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Content.Shared.BodySystem
|
||||||
|
{
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public class BiologicalDamageContainer : AbstractDamageContainer
|
||||||
|
{
|
||||||
|
public override List<DamageClass> SupportedDamageClasses {
|
||||||
|
get { return new List<DamageClass> { DamageClass.Brute, DamageClass.Burn, DamageClass.Toxin, DamageClass.Airloss }; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
102
Resources/Prototypes/BodySystem/BodyParts/humanoid_parts.yml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Torso.BasicHuman
|
||||||
|
name: "human torso"
|
||||||
|
plural: "human torsos"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: "torso_m"
|
||||||
|
partType: Torso
|
||||||
|
durability: 100
|
||||||
|
destroyThreshold: -150
|
||||||
|
resistance: 4.0
|
||||||
|
size: 14
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
mechanisms:
|
||||||
|
- mechanism.Heart.BasicHuman
|
||||||
|
- mechanism.Lungs.BasicHuman
|
||||||
|
- mechanism.Liver.BasicHuman
|
||||||
|
- mechanism.Kidneys.BasicHuman
|
||||||
|
|
||||||
|
|
||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Head.BasicHuman
|
||||||
|
name: "human head"
|
||||||
|
plural: "human heads"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: head_m
|
||||||
|
partType: Head
|
||||||
|
durability: 50
|
||||||
|
destroyThreshold: -120
|
||||||
|
resistance: 7.0
|
||||||
|
size: 7
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
mechanisms:
|
||||||
|
- mechanism.Brain.BasicHuman
|
||||||
|
- mechanism.Eyes.BasicHuman
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Arm.BasicHuman
|
||||||
|
name: "human arm"
|
||||||
|
plural: "human arms"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: r_arm
|
||||||
|
partType: Arm
|
||||||
|
durability: 40
|
||||||
|
destroyThreshold: -80
|
||||||
|
resistance: 3.0
|
||||||
|
size: 5
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
properties:
|
||||||
|
- !type:ArmLength
|
||||||
|
length: 2.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Hand.BasicHuman
|
||||||
|
name: "human hand"
|
||||||
|
plural: "human hands"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: r_hand
|
||||||
|
partType: Hand
|
||||||
|
durability: 30
|
||||||
|
destroyThreshold: -60
|
||||||
|
resistance: 2.0
|
||||||
|
size: 3
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
|
||||||
|
|
||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Leg.BasicHuman
|
||||||
|
name: "human leg"
|
||||||
|
plural: "human legs"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: r_leg
|
||||||
|
partType: Leg
|
||||||
|
durability: 45
|
||||||
|
destroyThreshold: -90
|
||||||
|
resistance: 2.0
|
||||||
|
size: 6
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
|
||||||
|
|
||||||
|
- type: bodyPart
|
||||||
|
id: bodyPart.Foot.BasicHuman
|
||||||
|
name: "human foot"
|
||||||
|
plural: "human feet"
|
||||||
|
rsiPath: Objects/BodySystem/BodyParts/basic_human.rsi
|
||||||
|
rsiState: r_foot
|
||||||
|
partType: Foot
|
||||||
|
durability: 30
|
||||||
|
destroyThreshold: -60
|
||||||
|
resistance: 3.0
|
||||||
|
size: 2
|
||||||
|
compatibility: Biological
|
||||||
|
surgeryDataType: BiologicalsurgeryDataType
|
||||||
|
|
||||||
15
Resources/Prototypes/BodySystem/BodyPresets/basic_human.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
- type: bodyPreset
|
||||||
|
name: "basic human"
|
||||||
|
id: bodyPreset.BasicHuman
|
||||||
|
partIDs:
|
||||||
|
head: bodyPart.Head.BasicHuman
|
||||||
|
torso: bodyPart.Torso.BasicHuman
|
||||||
|
right arm: bodyPart.Arm.BasicHuman
|
||||||
|
left arm: bodyPart.Arm.BasicHuman
|
||||||
|
right hand: bodyPart.Hand.BasicHuman
|
||||||
|
left hand: bodyPart.Hand.BasicHuman
|
||||||
|
right leg: bodyPart.Leg.BasicHuman
|
||||||
|
left leg: bodyPart.Leg.BasicHuman
|
||||||
|
right foot: bodyPart.Foot.BasicHuman
|
||||||
|
left foot: bodyPart.Foot.BasicHuman
|
||||||
|
|
||||||
33
Resources/Prototypes/BodySystem/BodyTemplates/humanoid.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
- type: bodyTemplate
|
||||||
|
id: bodyTemplate.Humanoid
|
||||||
|
name: "humanoid"
|
||||||
|
centerSlot: "torso"
|
||||||
|
slots:
|
||||||
|
head: Head
|
||||||
|
torso: Torso
|
||||||
|
left arm: Arm
|
||||||
|
left hand: Hand
|
||||||
|
right arm: Arm
|
||||||
|
right hand: Hand
|
||||||
|
left leg: Leg
|
||||||
|
left foot: Foot
|
||||||
|
right leg: Leg
|
||||||
|
right foot: Foot
|
||||||
|
connections:
|
||||||
|
head:
|
||||||
|
- torso
|
||||||
|
torso:
|
||||||
|
- left arm
|
||||||
|
- right arm
|
||||||
|
- left leg
|
||||||
|
- right leg
|
||||||
|
left arm:
|
||||||
|
- left hand
|
||||||
|
right arm:
|
||||||
|
- right hand
|
||||||
|
left leg:
|
||||||
|
- left foot
|
||||||
|
right leg:
|
||||||
|
- right foot
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
- type: bodyTemplate
|
||||||
|
id: bodyTemplate.Quadrupedal
|
||||||
|
name: "Quadrupedal"
|
||||||
|
centerSlot: "torso"
|
||||||
|
slots:
|
||||||
|
head: Head
|
||||||
|
torso: Torso
|
||||||
|
left front leg: Leg
|
||||||
|
left front paw: Foot
|
||||||
|
right front leg: Leg
|
||||||
|
right front paw: Foot
|
||||||
|
left hind leg: Leg
|
||||||
|
left hind paw: Foot
|
||||||
|
right hind leg: Leg
|
||||||
|
right hind paw: Foot
|
||||||
|
connections:
|
||||||
|
head:
|
||||||
|
- torso
|
||||||
|
torso:
|
||||||
|
- left front leg
|
||||||
|
- right front leg
|
||||||
|
- left hind leg
|
||||||
|
- right hind paw
|
||||||
|
left front leg:
|
||||||
|
- left front paw
|
||||||
|
right front leg:
|
||||||
|
- right front paw
|
||||||
|
left hind leg:
|
||||||
|
- left hind paw
|
||||||
|
right hind leg:
|
||||||
|
- right hind paw
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Brain.BasicHuman
|
||||||
|
name: "Human Brain"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "brain_human"
|
||||||
|
description: "The source of incredible, unending intelligence. Honk."
|
||||||
|
durability: 10
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Eyes.BasicHuman
|
||||||
|
name: "Human Eyes"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "eyes_human"
|
||||||
|
description: "Ocular organ capable of turning light into a colorful visual."
|
||||||
|
durability: 10
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Heart.BasicHuman
|
||||||
|
name: "Human Heart"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "heart_human"
|
||||||
|
description: "Pumps blood throughout a body. Essential for any entity with blood."
|
||||||
|
durability: 10
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Lungs.BasicHuman
|
||||||
|
name: "Human Lungs"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "lungs_human"
|
||||||
|
description: "Filters oxygen from an atmosphere, which is then sent into the bloodstream to be used as an electron carrier."
|
||||||
|
durability: 13
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Liver.BasicHuman
|
||||||
|
name: "Human Liver"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "liver_human"
|
||||||
|
description: "Filters impurities out of a bloodstream and provides other important functionality to a human."
|
||||||
|
durability: 15
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.Kidneys.BasicHuman
|
||||||
|
name: "Human Kidneys"
|
||||||
|
rsiPath: Objects/BodySystem/Organs/basic_human.rsi
|
||||||
|
rsiState: "kidneys_human"
|
||||||
|
description: "Filters toxins out of a bloodstream."
|
||||||
|
durability: 20
|
||||||
|
size: 1
|
||||||
|
compatibility: Biological
|
||||||
20
Resources/Prototypes/BodySystem/Mechanisms/basic_tests.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
- type: mechanism
|
||||||
|
id: mechanism.EMPStriker
|
||||||
|
name: "EMP Striker"
|
||||||
|
description: "When activated, this arm implant will apply a small EMP on the target of a physical strike for 10 watts per use."
|
||||||
|
durability: 80
|
||||||
|
size: 4
|
||||||
|
compatibility: Universal
|
||||||
|
implantableParts:
|
||||||
|
- Arm
|
||||||
|
- Hand
|
||||||
|
|
||||||
|
|
||||||
|
- type: mechanism
|
||||||
|
id: mechanism.HonkModule
|
||||||
|
name: "HONK Module 3000"
|
||||||
|
description: "Mandatory implant for all clowns after the Genevo Convention of 2459."
|
||||||
|
durability: 50
|
||||||
|
size: 3
|
||||||
|
compatibility: Universal
|
||||||
|
|
||||||
128
Resources/Prototypes/BodySystem/Surgery/surgery_tools.yml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
- type: entity
|
||||||
|
name: Scalpel
|
||||||
|
parent: BaseItem
|
||||||
|
id: scalpel
|
||||||
|
desc: A surgical tool used to make incisions into flesh.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: Incision
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: scalpel
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: scalpel
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Retractor
|
||||||
|
parent: BaseItem
|
||||||
|
id: retractor
|
||||||
|
desc: A surgical tool used to hold open incisions.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: Retraction
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: retractor
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: retractor
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Cautery
|
||||||
|
parent: BaseItem
|
||||||
|
id: cautery
|
||||||
|
desc: A surgical tool used to cauterize open wounds.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: Cauterization
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: cautery
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: cautery
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Drill
|
||||||
|
parent: BaseItem
|
||||||
|
id: drill
|
||||||
|
desc: A surgical drill for making holes into hard material.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: Drilling
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: drill
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: drill
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Bone Saw
|
||||||
|
parent: BaseItem
|
||||||
|
id: bone_saw
|
||||||
|
desc: A surgical tool used to cauterize open wounds.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: BoneSawing
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: bone_saw
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: bone_saw
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Hemostat
|
||||||
|
parent: BaseItem
|
||||||
|
id: hemostat
|
||||||
|
desc: A surgical tool used to compress blood vessels to prevent bleeding.
|
||||||
|
components:
|
||||||
|
- type: SurgeryTool
|
||||||
|
surgeryToolClass: VesselCompression
|
||||||
|
baseOperateTime: 3
|
||||||
|
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: hemostat
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Surgery/surgery_tools.rsi
|
||||||
|
state: hemostat
|
||||||
|
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MeleeWeapon
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
- type: entity
|
||||||
|
name: "BaseDroppedBodyPart"
|
||||||
|
parent: BaseItem
|
||||||
|
id: BaseDroppedBodyPart
|
||||||
|
components:
|
||||||
|
- type: DroppedBodyPart
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects\BodySystem\Organs\eyes_grey.png
|
||||||
|
- type: Icon
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: "BaseDroppedMechanism"
|
||||||
|
parent: BaseItem
|
||||||
|
id: BaseDroppedMechanism
|
||||||
|
components:
|
||||||
|
- type: DroppedMechanism
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects\BodySystem\Organs\eyes_grey.png
|
||||||
|
- type: Icon
|
||||||
@@ -166,6 +166,22 @@
|
|||||||
key: id_key
|
key: id_key
|
||||||
screen: id
|
screen: id
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: computerBodyScanner
|
||||||
|
parent: ComputerBase
|
||||||
|
name: Body Scanner Computer
|
||||||
|
components:
|
||||||
|
- type: BodyScanner
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.BodyScannerUiKey.Key
|
||||||
|
type: BodyScannerBoundUserInterface
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: ComputerVisualizer2D
|
||||||
|
key: generic_key
|
||||||
|
screen: generic
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: ComputerComms
|
id: ComputerComms
|
||||||
parent: ComputerBase
|
parent: ComputerBase
|
||||||
|
|||||||
@@ -110,6 +110,10 @@
|
|||||||
- type: Species
|
- type: Species
|
||||||
Template: Human
|
Template: Human
|
||||||
HeatResistance: 323
|
HeatResistance: 323
|
||||||
|
- type: BodyManager
|
||||||
|
BaseTemplate: bodyTemplate.Humanoid
|
||||||
|
BasePreset: bodyPreset.BasicHuman
|
||||||
|
|
||||||
- type: StatusEffectsUI
|
- type: StatusEffectsUI
|
||||||
- type: OverlayEffectsUI
|
- type: OverlayEffectsUI
|
||||||
- type: HeatResistance
|
- type: HeatResistance
|
||||||
@@ -235,3 +239,4 @@
|
|||||||
- type: SpeciesVisualizer2D
|
- type: SpeciesVisualizer2D
|
||||||
|
|
||||||
- type: HumanoidAppearance
|
- type: HumanoidAppearance
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 573 B |
|
After Width: | Height: | Size: 307 B |
|
After Width: | Height: | Size: 211 B |
|
After Width: | Height: | Size: 294 B |
|
After Width: | Height: | Size: 275 B |
@@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "head_m",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "torso_m",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "l_arm",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "r_arm",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "l_hand",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "r_hand",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "l_leg",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "r_leg",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "l_foot",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "r_foot",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[1.0], [1.0], [1.0], [1.0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 276 B |
|
After Width: | Height: | Size: 266 B |
|
After Width: | Height: | Size: 275 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 708 B |
|
After Width: | Height: | Size: 160 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 233 B |
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "brain_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eyes_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "heart_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kidneys_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "liver_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lungs_human",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/BodySystem/Organs/eyes_advanced.png
Normal file
|
After Width: | Height: | Size: 163 B |
BIN
Resources/Textures/Objects/BodySystem/Organs/eyes_grey.png
Normal file
|
After Width: | Height: | Size: 165 B |
|
After Width: | Height: | Size: 197 B |
BIN
Resources/Textures/Objects/BodySystem/Organs/lungs_advanced.png
Normal file
|
After Width: | Height: | Size: 336 B |
BIN
Resources/Textures/Objects/BodySystem/Organs/lungs_plasmaman.png
Normal file
|
After Width: | Height: | Size: 233 B |
BIN
Resources/Textures/Objects/BodySystem/Organs/lungs_vox.png
Normal file
|
After Width: | Height: | Size: 233 B |
|
After Width: | Height: | Size: 417 B |
BIN
Resources/Textures/Objects/Surgery/surgery_tools.rsi/cautery.png
Normal file
|
After Width: | Height: | Size: 171 B |
BIN
Resources/Textures/Objects/Surgery/surgery_tools.rsi/drill.png
Normal file
|
After Width: | Height: | Size: 272 B |
|
After Width: | Height: | Size: 142 B |
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "scalpel",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "retractor",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bone_saw",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hemostat",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "drill",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cautery",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[1.0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 212 B |
BIN
Resources/Textures/Objects/Surgery/surgery_tools.rsi/scalpel.png
Normal file
|
After Width: | Height: | Size: 342 B |