2020-05-13 14:48:49 -05:00
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>
2020-07-02 13:51:14 -05:00
/// Data class representing a singular limb such as an arm or a leg. Typically held within either a <see cref="BodyManagerComponent"/>,
/// which coordinates functions between BodyParts, or a <see cref="DroppedBodyPartComponent"/>.
2020-05-13 14:48:49 -05:00
/// </summary>
public class BodyPart
{
[ViewVariables]
private ISurgeryData _surgeryData ;
[ViewVariables]
private List < Mechanism > _mechanisms = new List < Mechanism > ( ) ;
[ViewVariables]
private int _sizeUsed = 0 ;
/// <summary>
2020-07-02 13:51:14 -05:00
/// The name of this BodyPart, often displayed to the user. For example, it could be named "advanced robotic arm".
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public string Name { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// Plural version of this BodyPart name.
2020-05-13 14:48:49 -05:00
/// </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>
2020-07-02 13:51:14 -05:00
/// <see cref="BodyPartType"/> that this BodyPart is considered to be. For example, BodyPartType.Arm.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public BodyPartType PartType { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// Max HP of this BodyPart.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public int MaxDurability { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// Current HP of this BodyPart based on sum of all damage types.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public int CurrentDurability = > MaxDurability - CurrentDamages . Damage ;
/// <summary>
/// Current damage dealt to this BodyPart.
/// </summary>
[ViewVariables]
public AbstractDamageContainer CurrentDamages { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// At what HP this BodyPartis completely destroyed.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public int DestroyThreshold { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// Armor of this BodyPart against attacks.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public float Resistance { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// Determines many things: how many mechanisms can be fit inside this BodyPart, whether a body can fit through tiny crevices, etc.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public int Size { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// 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.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public BodyPartCompatibility Compatibility { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// List of <see cref="IExposeData"/> properties, allowing for additional data classes to be attached to a limb, such as a "length" class to an arm.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public List < IExposeData > Properties { get ; set ; }
/// <summary>
2020-07-02 13:51:14 -05:00
/// List of all <see cref="Mechanism">Mechanisms</see> currently inside this BodyPart.
2020-05-13 14:48:49 -05:00
/// </summary>
[ViewVariables]
public List < Mechanism > Mechanisms = > _mechanisms ;
2020-07-02 13:51:14 -05:00
public BodyPart ( ) { }
2020-05-13 14:48:49 -05:00
public BodyPart ( BodyPartPrototype data )
{
LoadFromPrototype ( data ) ;
}
2020-07-02 13:51:14 -05:00
public bool CanAttachBodyPart ( BodyPart toBeConnected )
{
return _surgeryData . CanAttachBodyPart ( toBeConnected ) ;
}
2020-05-13 14:48:49 -05:00
/// <summary>
2020-07-02 13:51:14 -05:00
/// Returns whether the given <see cref="Mechanism"/> can be installed on this BodyPart.
2020-05-13 14:48:49 -05:00
/// </summary>
2020-07-02 13:51:14 -05:00
public bool CanInstallMechanism ( Mechanism mechanism )
2020-05-13 14:48:49 -05:00
{
if ( _sizeUsed + mechanism . Size > Size )
return false ; //No space
2020-07-02 13:51:14 -05:00
return _surgeryData . CanInstallMechanism ( mechanism ) ;
2020-05-13 14:48:49 -05:00
}
/// <summary>
2020-07-02 13:51:14 -05:00
/// Attempts to add a <see cref="Mechanism"/>. 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.
2020-05-13 14:48:49 -05:00
/// </summary>
2020-07-02 13:51:14 -05:00
public bool TryInstallMechanism ( Mechanism mechanism )
2020-05-13 14:48:49 -05:00
{
2020-07-02 13:51:14 -05:00
if ( CanInstallMechanism ( mechanism ) )
{
_mechanisms . Add ( mechanism ) ;
_sizeUsed + = mechanism . Size ;
return true ;
}
return false ;
}
/// <summary>
/// Attempts to install a <see cref="DroppedMechanismComponent"/> into the given limb, potentially deleting the dropped <see cref="IEntity"/>. Returns true if successful, false if there was an error (e.g. not enough room in BodyPart).
/// </summary>
public bool TryInstallDroppedMechanism ( DroppedMechanismComponent droppedMechanism )
{
if ( ! TryInstallMechanism ( droppedMechanism . ContainedMechanism ) )
return false ; //Installing the mechanism failed for some reason.
2020-05-13 14:48:49 -05:00
droppedMechanism . Owner . Delete ( ) ;
return true ;
}
/// <summary>
2020-07-02 13:51:14 -05:00
/// Tries to remove the given <see cref="Mechanism"/> 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 <see cref="DroppedMechanismComponent"/> on the newly spawned entity.
2020-05-13 14:48:49 -05:00
/// </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>
2020-07-02 13:51:14 -05:00
/// Tries to destroy the given <see cref="Mechanism"/> in the given BodyPart. Returns false if there was an error, true otherwise. Does NOT spawn a dropped entity.
2020-05-13 14:48:49 -05:00
/// </summary>
public bool DestroyMechanism ( BodyPart bodyPartTarget , Mechanism mechanismTarget )
{
if ( ! _mechanisms . Contains ( mechanismTarget ) )
return false ;
_mechanisms . Remove ( mechanismTarget ) ;
_sizeUsed - = mechanismTarget . Size ;
return true ;
}
2020-07-02 13:51:14 -05:00
2020-05-13 14:48:49 -05:00
/// <summary>
2020-07-02 13:51:14 -05:00
/// Returns whether the given <see cref="SurgeryType"/> can be used on the current state of this BodyPart.
2020-05-13 14:48:49 -05:00
/// </summary>
2020-07-02 13:51:14 -05:00
public bool SurgeryCheck ( SurgeryType toolType )
2020-05-13 14:48:49 -05:00
{
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>
2020-07-02 13:51:14 -05:00
public bool AttemptSurgery ( SurgeryType toolType , IBodyPartContainer target , ISurgeon surgeon , IEntity performer )
2020-05-13 14:48:49 -05:00
{
2020-07-02 13:51:14 -05:00
return _surgeryData . PerformSurgery ( toolType , target , surgeon , performer ) ;
2020-05-13 14:48:49 -05:00
}
2020-07-02 13:51:14 -05:00
2020-05-13 14:48:49 -05:00
/// <summary>
2020-07-02 13:51:14 -05:00
/// Loads the given <see cref="BodyPartPrototype"/> - current data on this <see cref="BodyPart"/> will be overwritten!
2020-05-13 14:48:49 -05:00
/// </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 ) ) ;
}
}
}
}