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 a BodyManagerComponent, /// which coordinates functions between BodyParts. /// public class BodyPart { [ViewVariables] private ISurgeryData _surgeryData; [ViewVariables] private List _mechanisms = new List(); [ViewVariables] private int _sizeUsed = 0; /// /// Body part name. /// [ViewVariables] public string Name { get; set; } /// /// Plural version of this body part's 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; } /// /// BodyPartType that this body part is considered. /// [ViewVariables] public BodyPartType PartType { get; set; } /// /// Max HP of this body part. /// [ViewVariables] public int MaxDurability { get; set; } /// /// Current HP of this body part 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 body part is completely destroyed. /// [ViewVariables] public int DestroyThreshold { get; set; } /// /// Armor of the body part against attacks. /// [ViewVariables] public float Resistance { get; set; } /// /// Determines many things: how many mechanisms can be fit inside a body part, fitting through tiny crevices, etc. /// [ViewVariables] public int Size { get; set; } /// /// 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. /// [ViewVariables] public BodyPartCompatibility Compatibility { get; set; } /// /// List of IExposeData 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); } /// /// 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. /// public bool InstallMechanism(Mechanism mechanism) { if (_sizeUsed + mechanism.Size > Size) return false; //No space _mechanisms.Add(mechanism); _sizeUsed += mechanism.Size; return true; } /// /// 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). /// public bool InstallDroppedMechanism(DroppedMechanismComponent droppedMechanism) { if (_sizeUsed + droppedMechanism.ContainedMechanism.Size > Size) return false; //No space InstallMechanism(droppedMechanism.ContainedMechanism); droppedMechanism.Owner.Delete(); return true; } /// /// 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. /// 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 Mechanism 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 SurgertToolType can be used on the current state of this BodyPart (e.g. /// public bool SurgeryCheck(SurgeryToolType 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(SurgeryToolType toolType, BodyManagerComponent target, IEntity performer) { return _surgeryData.PerformSurgery(toolType, target, performer); } /// /// Loads the given BodyPartPrototype - current data on this BodyPart 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)); } } } }