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 { /// /// Data class representing a singular limb such as an arm or a leg. Typically held within either a , /// which coordinates functions between BodyParts, or a . /// public class BodyPart { [ViewVariables] private ISurgeryData _surgeryData; [ViewVariables] private List _mechanisms = new List(); [ViewVariables] private int _sizeUsed = 0; /// /// The name of this BodyPart, often displayed to the user. For example, it could be named "advanced robotic arm". /// [ViewVariables] public string Name { get; set; } /// /// Plural version of this BodyPart name. /// [ViewVariables] public string Plural { get; set; } /// /// Path to the RSI that represents this BodyPart. /// [ViewVariables] public string RSIPath { get; set; } /// /// RSI state that represents this BodyPart. /// [ViewVariables] public string RSIState { get; set; } /// /// that this BodyPart is considered to be. For example, BodyPartType.Arm. /// [ViewVariables] public BodyPartType PartType { get; set; } /// /// Max HP of this BodyPart. /// [ViewVariables] public int MaxDurability { get; set; } /// /// Current HP of this BodyPart based on sum of all damage types. /// [ViewVariables] public int CurrentDurability => MaxDurability - CurrentDamages.Damage; /// /// Current damage dealt to this BodyPart. /// [ViewVariables] public AbstractDamageContainer CurrentDamages { get; set; } /// /// At what HP this BodyPartis completely destroyed. /// [ViewVariables] public int DestroyThreshold { get; set; } /// /// Armor of this BodyPart against attacks. /// [ViewVariables] public float Resistance { get; set; } /// /// Determines many things: how many mechanisms can be fit inside this BodyPart, whether a body can fit through tiny crevices, etc. /// [ViewVariables] public int Size { get; set; } /// /// What types of BodyParts this BodyPart can easily attach to. For the most part, most limbs aren't universal and require extra work to attach between types. /// [ViewVariables] public BodyPartCompatibility Compatibility { get; set; } /// /// List of properties, allowing for additional data classes to be attached to a limb, such as a "length" class to an arm. /// [ViewVariables] public List Properties { get; set; } /// /// List of all Mechanisms currently inside this BodyPart. /// [ViewVariables] public List Mechanisms => _mechanisms; public BodyPart() { } public BodyPart(BodyPartPrototype data) { LoadFromPrototype(data); } public bool CanAttachBodyPart(BodyPart toBeConnected) { return _surgeryData.CanAttachBodyPart(toBeConnected); } /// /// Returns whether the given can be installed on this BodyPart. /// public bool CanInstallMechanism(Mechanism mechanism) { if (_sizeUsed + mechanism.Size > Size) return false; //No space return _surgeryData.CanInstallMechanism(mechanism); } /// /// Attempts to add a . Returns true if successful, false if there was an error (e.g. not enough room in BodyPart). Call InstallDroppedMechanism instead if you want to easily install an IEntity with a DroppedMechanismComponent. /// public bool TryInstallMechanism(Mechanism mechanism) { if (CanInstallMechanism(mechanism)) { _mechanisms.Add(mechanism); _sizeUsed += mechanism.Size; return true; } return false; } /// /// Attempts to install a into the given limb, potentially deleting the dropped . Returns true if successful, false if there was an error (e.g. not enough room in BodyPart). /// public bool TryInstallDroppedMechanism(DroppedMechanismComponent droppedMechanism) { if (!TryInstallMechanism(droppedMechanism.ContainedMechanism)) return false; //Installing the mechanism failed for some reason. droppedMechanism.Owner.Delete(); return true; } /// /// Tries to remove the given reference from this BodyPart. Returns null if there was an error in spawning the entity or removing the mechanism, otherwise returns a reference to the on the newly spawned entity. /// public DroppedMechanismComponent DropMechanism(IEntity dropLocation, Mechanism mechanismTarget) { if (!_mechanisms.Contains(mechanismTarget)) return null; _mechanisms.Remove(mechanismTarget); _sizeUsed -= mechanismTarget.Size; IEntityManager entityManager = IoCManager.Resolve(); var mechanismEntity = entityManager.SpawnEntity("BaseDroppedMechanism", dropLocation.Transform.GridPosition); var droppedMechanism = mechanismEntity.GetComponent(); droppedMechanism.InitializeDroppedMechanism(mechanismTarget); return droppedMechanism; } /// /// Tries to destroy the given in the given BodyPart. Returns false if there was an error, true otherwise. Does NOT spawn a dropped entity. /// public bool DestroyMechanism(BodyPart bodyPartTarget, Mechanism mechanismTarget) { if (!_mechanisms.Contains(mechanismTarget)) return false; _mechanisms.Remove(mechanismTarget); _sizeUsed -= mechanismTarget.Size; return true; } /// /// Returns whether the given can be used on the current state of this BodyPart. /// public bool SurgeryCheck(SurgeryType toolType) { return _surgeryData.CheckSurgery(toolType); } /// /// Attempts to perform surgery on this BodyPart with the given tool. Returns false if there was an error, true if successful. /// public bool AttemptSurgery(SurgeryType toolType, IBodyPartContainer target, ISurgeon surgeon, IEntity performer) { return _surgeryData.PerformSurgery(toolType, target, surgeon, performer); } /// /// Loads the given - current data on this will be overwritten! /// 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(); 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)); } } } }