Re-organize all projects (#4166)

This commit is contained in:
DrSmugleaf
2021-06-09 22:19:39 +02:00
committed by GitHub
parent 9f50e4061b
commit ff1a2d97ea
1773 changed files with 5258 additions and 5508 deletions

View File

@@ -1,81 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Access
{
public class SharedIdCardConsoleComponent : Component
{
public override string Name => "IdCardConsole";
public enum UiButton
{
PrivilegedId,
TargetId,
}
[Serializable, NetSerializable]
public class IdButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public IdButtonPressedMessage(UiButton button)
{
Button = button;
}
}
[Serializable, NetSerializable]
public class WriteToTargetIdMessage : BoundUserInterfaceMessage
{
public readonly string FullName;
public readonly string JobTitle;
public readonly List<string> AccessList;
public WriteToTargetIdMessage(string fullName, string jobTitle, List<string> accessList)
{
FullName = fullName;
JobTitle = jobTitle;
AccessList = accessList;
}
}
[Serializable, NetSerializable]
public class IdCardConsoleBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly string PrivilegedIdName;
public readonly bool IsPrivilegedIdPresent;
public readonly bool IsPrivilegedIdAuthorized;
public readonly bool IsTargetIdPresent;
public readonly string TargetIdName;
public readonly string? TargetIdFullName;
public readonly string? TargetIdJobTitle;
public readonly string[]? TargetIdAccessList;
public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized,
bool isTargetIdPresent,
string? targetIdFullName,
string? targetIdJobTitle,
string[]? targetIdAccessList, string privilegedIdName, string targetIdName)
{
IsPrivilegedIdPresent = isPrivilegedIdPresent;
IsPrivilegedIdAuthorized = isPrivilegedIdAuthorized;
IsTargetIdPresent = isTargetIdPresent;
TargetIdFullName = targetIdFullName;
TargetIdJobTitle = targetIdJobTitle;
TargetIdAccessList = targetIdAccessList;
PrivilegedIdName = privilegedIdName;
TargetIdName = targetIdName;
}
}
[Serializable, NetSerializable]
public enum IdCardConsoleUiKey
{
Key,
}
}
}

View File

@@ -1,54 +0,0 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Pulling;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.ActionBlocking
{
public class SharedCuffableComponent : Component, IActionBlocker
{
public override string Name => "Cuffable";
public override uint? NetID => ContentNetIDs.CUFFED;
[ComponentDependency] private readonly SharedPullableComponent? _pullable = default!;
[ViewVariables]
public bool CanStillInteract { get; set; } = true;
#region ActionBlockers
bool IActionBlocker.CanInteract() => CanStillInteract;
bool IActionBlocker.CanUse() => CanStillInteract;
bool IActionBlocker.CanPickup() => CanStillInteract;
bool IActionBlocker.CanDrop() => CanStillInteract;
bool IActionBlocker.CanAttack() => CanStillInteract;
bool IActionBlocker.CanEquip() => CanStillInteract;
bool IActionBlocker.CanUnequip() => CanStillInteract;
bool IActionBlocker.CanMove() => _pullable == null || !_pullable.BeingPulled || CanStillInteract;
#endregion
[Serializable, NetSerializable]
protected sealed class CuffableComponentState : ComponentState
{
public bool CanStillInteract { get; }
public int NumHandsCuffed { get; }
public string? RSI { get; }
public string IconState { get; }
public Color Color { get; }
public CuffableComponentState(int numHandsCuffed, bool canStillInteract, string? rsiPath, string iconState, Color color) : base(ContentNetIDs.CUFFED)
{
NumHandsCuffed = numHandsCuffed;
CanStillInteract = canStillInteract;
RSI = rsiPath;
IconState = iconState;
Color = color;
}
}
}
}

View File

@@ -1,24 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.ActionBlocking
{
public abstract class SharedHandcuffComponent : Component
{
public override string Name => "Handcuff";
public override uint? NetID => ContentNetIDs.HANDCUFFS;
[Serializable, NetSerializable]
protected sealed class HandcuffedComponentState : ComponentState
{
public string? IconState { get; }
public HandcuffedComponentState(string? iconState) : base(ContentNetIDs.HANDCUFFS)
{
IconState = iconState;
}
}
}
}

View File

@@ -1,38 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Objectives;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Actor
{
public class SharedCharacterInfoComponent : Component
{
public override string Name => "CharacterInfo";
public override uint? NetID => ContentNetIDs.CHARACTERINFO;
[Serializable, NetSerializable]
protected class RequestCharacterInfoMessage : ComponentMessage
{
public RequestCharacterInfoMessage()
{
Directed = true;
}
}
[Serializable, NetSerializable]
protected class CharacterInfoMessage : ComponentMessage
{
public readonly Dictionary<string, List<ConditionInfo>> Objectives;
public readonly string JobTitle;
public CharacterInfoMessage(string jobTitle, Dictionary<string, List<ConditionInfo>> objectives)
{
Directed = true;
JobTitle = jobTitle;
Objectives = objectives;
}
}
}
}

View File

@@ -1,96 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Arcade
{
public class SharedSpaceVillainArcadeComponent : Component
{
public override string Name => "SpaceVillainArcade";
public override uint? NetID => ContentNetIDs.SPACE_VILLAIN_ARCADE;
[Serializable, NetSerializable]
public enum Indicators
{
/// <summary>
/// Blinks when any invincible flag is set
/// </summary>
HealthManager,
/// <summary>
/// Blinks when Overflow flag is set
/// </summary>
HealthLimiter
}
[Serializable, NetSerializable]
public enum PlayerAction
{
Attack,
Heal,
Recharge,
NewGame,
RequestData
}
[Serializable, NetSerializable]
public enum SpaceVillainArcadeVisualState
{
Normal,
Off,
Broken,
Win,
GameOver,
}
[Serializable, NetSerializable]
public enum SpaceVillainArcadeUiKey
{
Key,
}
[Serializable, NetSerializable]
public class SpaceVillainArcadePlayerActionMessage : BoundUserInterfaceMessage
{
public readonly PlayerAction PlayerAction;
public SpaceVillainArcadePlayerActionMessage(PlayerAction playerAction)
{
PlayerAction = playerAction;
}
}
[Serializable, NetSerializable]
public class SpaceVillainArcadeMetaDataUpdateMessage : SpaceVillainArcadeDataUpdateMessage
{
public readonly string GameTitle;
public readonly string EnemyName;
public readonly bool ButtonsDisabled;
public SpaceVillainArcadeMetaDataUpdateMessage(int playerHp, int playerMp, int enemyHp, int enemyMp, string playerActionMessage, string enemyActionMessage, string gameTitle, string enemyName, bool buttonsDisabled) : base(playerHp, playerMp, enemyHp, enemyMp, playerActionMessage, enemyActionMessage)
{
GameTitle = gameTitle;
EnemyName = enemyName;
ButtonsDisabled = buttonsDisabled;
}
}
[Serializable, NetSerializable]
public class SpaceVillainArcadeDataUpdateMessage : BoundUserInterfaceMessage
{
public readonly int PlayerHP;
public readonly int PlayerMP;
public readonly int EnemyHP;
public readonly int EnemyMP;
public readonly string PlayerActionMessage;
public readonly string EnemyActionMessage;
public SpaceVillainArcadeDataUpdateMessage(int playerHp, int playerMp, int enemyHp, int enemyMp, string playerActionMessage, string enemyActionMessage)
{
PlayerHP = playerHp;
PlayerMP = playerMp;
EnemyHP = enemyHp;
EnemyMP = enemyMp;
EnemyActionMessage = enemyActionMessage;
PlayerActionMessage = playerActionMessage;
}
}
}
}

View File

@@ -1,4 +1,5 @@
#nullable enable
using Content.Shared.NetIDs;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Atmos.GasTank

View File

@@ -1,141 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Body.Behavior
{
/// <summary>
/// Gives functionality to a <see cref="IMechanism"/> when added to it.
/// </summary>
[ImplicitDataDefinitionForInheritors]
public interface IMechanismBehavior
{
/// <summary>
/// The body that owns the <see cref="IBodyPart"/> in which the
/// <see cref="IMechanism"/> that owns this
/// <see cref="IMechanismBehavior"/> is in.
/// </summary>
IBody? Body { get; }
/// <summary>
/// The part in which the <see cref="IMechanism"/> that owns this
/// <see cref="IMechanismBehavior"/> is in.
/// </summary>
IBodyPart? Part { get; }
/// <summary>
/// Upward reference to the parent <see cref="IMechanism"/> that this
/// behavior is attached to.
/// </summary>
IMechanism Parent { get; }
/// <summary>
/// The entity that owns <see cref="Parent"/>.
/// For the entity owning the body that this mechanism may be in,
/// see <see cref="Body"/>'s <see cref="IBody.Owner"/>.
/// </summary>
IEntity Owner { get; }
/// <summary>
/// Called when this <see cref="IMechanismBehavior"/> is added to a
/// <see cref="IMechanism"/>, during <see cref="IComponent.Initialize"/>.
/// If it is added after component initialization,
/// it is called immediately.
/// </summary>
/// <param name="parent">
/// The mechanism that owns this <see cref="IMechanismBehavior"/>.
/// </param>
void Initialize(IMechanism parent);
/// <summary>
/// Called when this <see cref="IMechanismBehavior"/> is added to a
/// <see cref="IMechanism"/>, during <see cref="IComponent.Startup"/>.
/// If it is added after component startup, it is called immediately.
/// </summary>
void Startup();
/// <summary>
/// Runs an update cycle on this <see cref="IMechanismBehavior"/>.
/// </summary>
/// <param name="frameTime">
/// The amount of seconds that passed since the last update.
/// </param>
void Update(float frameTime);
/// <summary>
/// Called when the containing <see cref="IBodyPart"/> is attached to a
/// <see cref="IBody"/>.
/// For instance, attaching a head with a brain inside to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="body">
/// The body that the containing <see cref="IMechanism"/> was added to.
/// </param>
void AddedToBody(IBody body);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is
/// added into a <see cref="IBodyPart"/> that is not attached to a
/// <see cref="IBody"/>.
/// For instance, adding a brain to a dismembered head.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="part">
/// The part that the containing <see cref="IMechanism"/> was added to.
/// </param>
void AddedToPart(IBodyPart part);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is added to a
/// <see cref="IBodyPart"/> that is attached to a <see cref="IBody"/>.
/// For instance, adding a brain to a head that is attached to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="body">
/// The body that the containing <see cref="IMechanism"/> was added to.
/// </param>
/// <param name="part">
/// The part that the containing <see cref="IMechanism"/> was added to.
/// </param>
void AddedToPartInBody(IBody body, IBodyPart part);
/// <summary>
/// Called when the parent <see cref="IBodyPart"/> is removed from a
/// <see cref="IBody"/>.
/// For instance, removing a head with a brain inside from a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="old">
/// The body that the containing <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromBody(IBody old);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is
/// removed from a <see cref="IBodyPart"/> that is not attached to a
/// <see cref="IBody"/>.
/// For instance, removing a brain from a dismembered head.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="old">
/// The part that the containing <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromPart(IBodyPart old);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is removed from a
/// <see cref="IBodyPart"/> that is attached to a <see cref="IBody"/>.
/// For instance, removing a brain from a head that is attached to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="oldBody">
/// The body that the containing <see cref="IMechanism"/> was removed from.
/// </param>
/// <param name="oldPart">
/// The part that the containing <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromPartInBody(IBody oldBody, IBodyPart oldPart);
}
}

View File

@@ -1,256 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Body.Part;
using Content.Shared.GameObjects.Components.Body.Part.Property;
using Content.Shared.GameObjects.Components.Body.Preset;
using Content.Shared.GameObjects.Components.Body.Slot;
using Content.Shared.GameObjects.Components.Body.Template;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body
{
/// <summary>
/// Component representing a collection of <see cref="IBodyPart"/>s
/// attached to each other.
/// </summary>
public interface IBody : IComponent, IBodyPartContainer
{
/// <summary>
/// The <see cref="BodyTemplatePrototype"/> used to create this
/// <see cref="IBody"/>.
/// </summary>
public BodyTemplatePrototype? Template { get; }
/// <summary>
/// The <see cref="BodyPresetPrototype"/> used to create this
/// <see cref="IBody"/>.
/// </summary>
public BodyPresetPrototype? Preset { get; }
/// <summary>
/// An enumeration of the slots that make up this body, regardless
/// of if they contain a part or not.
/// </summary>
IEnumerable<BodyPartSlot> Slots { get; }
/// <summary>
/// An enumeration of the parts on this body paired with the slots
/// that they are in.
/// </summary>
IEnumerable<KeyValuePair<IBodyPart, BodyPartSlot>> Parts { get; }
/// <summary>
/// An enumeration of the slots on this body without a part in them.
/// </summary>
IEnumerable<BodyPartSlot> EmptySlots { get; }
/// <summary>
/// Finds the central <see cref="BodyPartSlot"/>, if any,
/// of this body.
/// </summary>
/// <returns>
/// The central <see cref="BodyPartSlot"/> if one exists,
/// null otherwise.
/// </returns>
BodyPartSlot? CenterSlot { get; }
/// <summary>
/// Finds the central <see cref="IBodyPart"/>, if any,
/// of this body.
/// </summary>
/// <returns>
/// The central <see cref="IBodyPart"/> if one exists,
/// null otherwise.
/// </returns>
IBodyPart? CenterPart { get; }
// TODO BODY Sensible templates
/// <summary>
/// Attempts to add a part to the given slot.
/// </summary>
/// <param name="slotId">The slot to add this part to.</param>
/// <param name="part">The part to add.</param>
/// <param name="checkSlotExists">
/// Whether to check if the slot exists, or create one otherwise.
/// </param>
/// <returns>
/// true if the part was added, false otherwise even if it was
/// already added.
/// </returns>
bool TryAddPart(string slotId, IBodyPart part);
void SetPart(string slotId, IBodyPart part);
/// <summary>
/// Checks if there is a <see cref="IBodyPart"/> in the given slot.
/// </summary>
/// <param name="slotId">The slot to look in.</param>
/// <returns>
/// true if there is a part in the given <see cref="slotId"/>,
/// false otherwise.
/// </returns>
bool HasPart(string slotId);
/// <summary>
/// Checks if this <see cref="IBody"/> contains the given <see cref="part"/>.
/// </summary>
/// <param name="part">The part to look for.</param>
/// <returns>
/// true if the given <see cref="part"/> is attached to the body,
/// false otherwise.
/// </returns>
bool HasPart(IBodyPart part);
/// <summary>
/// Removes the given <see cref="IBodyPart"/> from this body,
/// dropping other <see cref="IBodyPart"/> if they were hanging
/// off of it.
/// <param name="part">The part to remove.</param>
/// <returns>
/// true if the part was removed, false otherwise
/// even if the part was already removed previously.
/// </returns>
/// </summary>
bool RemovePart(IBodyPart part);
/// <summary>
/// Removes the body part in slot <see cref="slotId"/> from this body,
/// if one exists.
/// </summary>
/// <param name="slotId">The slot to remove it from.</param>
/// <returns>true if the part was removed, false otherwise.</returns>
bool RemovePart(string slotId);
/// <summary>
/// Removes the body part from this body, if one exists.
/// </summary>
/// <param name="part">The part to remove from this body.</param>
/// <param name="slotId">The slot that the part was in, if any.</param>
/// <returns>
/// true if <see cref="part"/> was removed, false otherwise.
/// </returns>
bool RemovePart(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slotId);
/// <summary>
/// Disconnects the given <see cref="IBodyPart"/> reference, potentially
/// dropping other <see cref="IBodyPart">BodyParts</see> if they
/// were hanging off of it.
/// </summary>
/// <param name="slot">The part to drop.</param>
/// <param name="dropped">
/// All of the parts that were dropped, including the one in
/// <see cref="slot"/>.
/// </param>
/// <returns>
/// true if the part was dropped, false otherwise.
/// </returns>
bool TryDropPart(BodyPartSlot slot, [NotNullWhen(true)] out Dictionary<BodyPartSlot, IBodyPart>? dropped);
/// <summary>
/// Recursively searches for if <see cref="part"/> is connected to
/// the center.
/// </summary>
/// <param name="part">The body part to find the center for.</param>
/// <returns>
/// true if it is connected to the center, false otherwise.
/// </returns>
bool ConnectedToCenter(IBodyPart part);
/// <summary>
/// Returns whether the given part slot exists in this body.
/// </summary>
/// <param name="slot">The slot to check for.</param>
/// <returns>true if the slot exists in this body, false otherwise.</returns>
bool HasSlot(string slot);
BodyPartSlot? GetSlot(IBodyPart part);
/// <summary>
/// Finds the slot that the given <see cref="IBodyPart"/> resides in.
/// </summary>
/// <param name="part">
/// The <see cref="IBodyPart"/> to find the slot for.
/// </param>
/// <param name="slot">The slot found, if any.</param>
/// <returns>true if a slot was found, false otherwise</returns>
bool TryGetSlot(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slot);
/// <summary>
/// Finds the <see cref="IBodyPart"/> in the given
/// <see cref="slotId"/> if one exists.
/// </summary>
/// <param name="slotId">The part slot to search in.</param>
/// <param name="result">The body part in that slot, if any.</param>
/// <returns>true if found, false otherwise.</returns>
bool TryGetPart(string slotId, [NotNullWhen(true)] out IBodyPart? result);
/// <summary>
/// Checks if a slot of the specified type exists on this body.
/// </summary>
/// <param name="type">The type to check for.</param>
/// <returns>true if present, false otherwise.</returns>
bool HasSlotOfType(BodyPartType type);
/// <summary>
/// Gets all slots of the specified type on this body.
/// </summary>
/// <param name="type">The type to check for.</param>
/// <returns>An enumerable of the found slots.</returns>
IEnumerable<BodyPartSlot> GetSlotsOfType(BodyPartType type);
/// <summary>
/// Checks if a part of the specified type exists on this body.
/// </summary>
/// <param name="type">The type to check for.</param>
/// <returns>true if present, false otherwise.</returns>
bool HasPartOfType(BodyPartType type);
/// <summary>
/// Gets all slots of the specified type on this body.
/// </summary>
/// <param name="type">The type to check for.</param>
/// <returns>An enumerable of the found slots.</returns>
IEnumerable<IBodyPart> GetPartsOfType(BodyPartType type);
/// <summary>
/// Finds all <see cref="IBodyPart"/>s with the given property in
/// this body.
/// </summary>
/// <type name="type">The property type to look for.</type>
/// <returns>A list of parts with that property.</returns>
IEnumerable<(IBodyPart part, IBodyPartProperty property)> GetPartsWithProperty(Type type);
/// <summary>
/// Finds all <see cref="IBodyPart"/>s with the given property in this body.
/// </summary>
/// <typeparam name="T">The property type to look for.</typeparam>
/// <returns>A list of parts with that property.</returns>
IEnumerable<(IBodyPart part, T property)> GetPartsWithProperty<T>() where T : class, IBodyPartProperty;
// TODO BODY Make a slot object that makes sense to the human mind, and make it serializable. Imagine the possibilities!
/// <summary>
/// Retrieves the slot at the given index.
/// </summary>
/// <param name="index">The index to look in.</param>
/// <returns>A pair of the slot name and part type occupying it.</returns>
BodyPartSlot SlotAt(int index);
/// <summary>
/// Retrieves the part at the given index.
/// </summary>
/// <param name="index">The index to look in.</param>
/// <returns>A pair of the part name and body part occupying it.</returns>
KeyValuePair<IBodyPart, BodyPartSlot> PartAt(int index);
/// <summary>
/// Gibs this body.
/// </summary>
/// <param name="gibParts">
/// Whether or not to also gib this body's parts.
/// </param>
void Gib(bool gibParts = false);
}
}

View File

@@ -1,177 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Mechanism
{
public interface IMechanism : IComponent
{
/// <summary>
/// The body that owns the <see cref="IBodyPart"/> in which this
/// <see cref="IMechanism"/> is in.
/// </summary>
IBody? Body { get; }
/// <summary>
/// The part in which this <see cref="IMechanism"/> is in.
/// </summary>
IBodyPart? Part { get; set; }
/// <summary>
/// The behaviors attached to this <see cref="IMechanism"/>
/// mapped by their type.
/// </summary>
IReadOnlyDictionary<Type, IMechanismBehavior> Behaviors { get; }
/// <summary>
/// Max HP of this <see cref="IMechanism"/>.
/// </summary>
int MaxDurability { get; set; }
/// <summary>
/// Current HP of this <see cref="IMechanism"/>.
/// </summary>
int CurrentDurability { get; set; }
/// <summary>
/// At what HP this <see cref="IMechanism"/> is completely destroyed.
/// </summary>
int DestroyThreshold { get; set; }
/// <summary>
/// Armor of this <see cref="IMechanism"/> against attacks.
/// </summary>
int Resistance { get; set; }
/// <summary>
/// Determines a handful of things - mostly whether this
/// <see cref="IMechanism"/> can fit into a <see cref="IBodyPart"/>.
/// </summary>
// TODO BODY OnSizeChanged
int Size { get; set; }
/// <summary>
/// What kind of <see cref="IBodyPart"/> this
/// <see cref="IMechanism"/> can be easily installed into.
/// </summary>
BodyPartCompatibility Compatibility { get; set; }
/// <summary>
/// Adds a <see cref="IMechanismBehavior"/> if this
/// <see cref="IMechanism"/> does not have it already.
/// </summary>
/// <typeparam name="T">The behavior type to add.</typeparam>
/// <returns>
/// True if the behavior already existed, false if it had to be created.
/// </returns>
bool EnsureBehavior<T>(out T behavior) where T : IMechanismBehavior, new();
/// <summary>
/// Checks if this <see cref="IMechanism"/> has the specified
/// <see cref="IMechanismBehavior"/>.
/// </summary>
/// <typeparam name="T">
/// The type of <see cref="IMechanismBehavior"/> to check for.
/// </typeparam>
/// <returns>
/// true if it has the <see cref="IMechanismBehavior"/>, false otherwise.
/// </returns>
bool HasBehavior<T>() where T : IMechanismBehavior;
/// <summary>
/// Removes the specified <see cref="IMechanismBehavior"/> from this
/// <see cref="IMechanism"/> if it has it.
/// </summary>
/// <typeparam name="T">
/// The type of <see cref="IMechanismBehavior"/> to remove.
/// </typeparam>
/// <returns>true if it was removed, false otherwise.</returns>
bool TryRemoveBehavior<T>() where T : IMechanismBehavior;
/// <summary>
/// Runs an update cycle for this <see cref="IMechanism"/>.
/// </summary>
/// <param name="frameTime">
/// The amount of seconds that passed since the last update.
/// </param>
void Update(float frameTime);
// TODO BODY Turn these into event listeners so they dont need to be exposed
/// <summary>
/// Called when the containing <see cref="IBodyPart"/> is attached to a
/// <see cref="IBody"/>.
/// For instance, attaching a head with a brain inside to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="body">
/// The body that this <see cref="IMechanism"/> was added to.
/// </param>
void AddedToBody(IBody body);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is
/// added into a <see cref="IBodyPart"/> that is not attached to a
/// <see cref="IBody"/>.
/// For instance, adding a brain to a dismembered head.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="part">
/// The part that this <see cref="IMechanism"/> was added to.
/// </param>
void AddedToPart(IBodyPart part);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is added to a
/// <see cref="IBodyPart"/> that is attached to a <see cref="IBody"/>.
/// For instance, adding a brain to a head that is attached to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="body">
/// The body that this <see cref="IMechanism"/> was added to.
/// </param>
/// <param name="part">
/// The part that this <see cref="IMechanism"/> was added to.
/// </param>
void AddedToPartInBody(IBody body, IBodyPart part);
/// <summary>
/// Called when the parent <see cref="IBodyPart"/> is removed from a
/// <see cref="IBody"/>.
/// For instance, removing a head with a brain inside from a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="old">
/// The body that this <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromBody(IBody old);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is
/// removed from a <see cref="IBodyPart"/> that is not attached to a
/// <see cref="IBody"/>.
/// For instance, removing a brain from a dismembered head.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="old">
/// The part that this <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromPart(IBodyPart old);
/// <summary>
/// Called when the parent <see cref="IMechanism"/> is removed from a
/// <see cref="IBodyPart"/> that is attached to a <see cref="IBody"/>.
/// For instance, removing a brain from a head that is attached to a body.
/// DO NOT CALL THIS DIRECTLY FROM OUTSIDE BODY SYSTEM CODE!
/// </summary>
/// <param name="oldBody">
/// The body that this <see cref="IMechanism"/> was removed from.
/// </param>
/// <param name="oldPart">
/// The part that this <see cref="IMechanism"/> was removed from.
/// </param>
void RemovedFromPartInBody(IBody oldBody, IBodyPart oldPart);
}
}

View File

@@ -1,242 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.Components.Body.Mechanism
{
public abstract class SharedMechanismComponent : Component, IMechanism, ISerializationHooks
{
public override string Name => "Mechanism";
protected readonly Dictionary<int, object> OptionsCache = new();
protected IBody? BodyCache;
protected int IdHash;
protected IEntity? PerformerCache;
private IBodyPart? _part;
[DataField("behaviors", serverOnly: true)] private HashSet<IMechanismBehavior> _behaviorTypes = new();
private readonly Dictionary<Type, IMechanismBehavior> _behaviors = new();
public IBody? Body => Part?.Body;
public IBodyPart? Part
{
get => _part;
set
{
if (_part == value)
{
return;
}
var old = _part;
_part = value;
if (old != null)
{
if (old.Body == null)
{
RemovedFromPart(old);
}
else
{
RemovedFromPartInBody(old.Body, old);
}
}
if (value != null)
{
if (value.Body == null)
{
AddedToPart(value);
}
else
{
AddedToPartInBody(value.Body, value);
}
}
}
}
public IReadOnlyDictionary<Type, IMechanismBehavior> Behaviors => _behaviors;
[DataField("maxDurability")] public int MaxDurability { get; set; } = 10;
[DataField("currentDurability")] public int CurrentDurability { get; set; } = 10;
[DataField("destroyThreshold")] public int DestroyThreshold { get; set; } = -10;
// TODO BODY: Surgery description and adding a message to the examine tooltip of the entity that owns this mechanism
// TODO BODY
[DataField("resistance")] public int Resistance { get; set; } = 0;
// TODO BODY OnSizeChanged
[DataField("size")] public int Size { get; set; } = 1;
[DataField("compatibility")]
public BodyPartCompatibility Compatibility { get; set; } = BodyPartCompatibility.Universal;
void ISerializationHooks.BeforeSerialization()
{
_behaviorTypes = _behaviors.Values.ToHashSet();
}
void ISerializationHooks.AfterDeserialization()
{
foreach (var behavior in _behaviorTypes)
{
var type = behavior.GetType();
if (!_behaviors.TryAdd(type, behavior))
{
Logger.Warning($"Duplicate behavior in {nameof(SharedMechanismComponent)}: {type}.");
continue;
}
IoCManager.InjectDependencies(behavior);
}
}
public override void Initialize()
{
base.Initialize();
foreach (var behavior in _behaviors.Values)
{
behavior.Initialize(this);
}
}
protected override void Startup()
{
base.Startup();
foreach (var behavior in _behaviors.Values)
{
behavior.Startup();
}
}
public bool EnsureBehavior<T>(out T behavior) where T : IMechanismBehavior, new()
{
if (_behaviors.TryGetValue(typeof(T), out var rawBehavior))
{
behavior = (T) rawBehavior;
return true;
}
behavior = IoCManager.Resolve<IDynamicTypeFactory>().CreateInstance<T>();
_behaviors.Add(typeof(T), behavior);
behavior.Initialize(this);
behavior.Startup();
return false;
}
public bool HasBehavior<T>() where T : IMechanismBehavior
{
return _behaviors.ContainsKey(typeof(T));
}
public bool TryRemoveBehavior<T>() where T : IMechanismBehavior
{
return _behaviors.Remove(typeof(T));
}
public void Update(float frameTime)
{
foreach (var behavior in _behaviors.Values)
{
behavior.Update(frameTime);
}
}
public void AddedToBody(IBody body)
{
DebugTools.AssertNotNull(Body);
DebugTools.AssertNotNull(body);
foreach (var behavior in _behaviors.Values)
{
behavior.AddedToBody(body);
}
}
public void AddedToPart(IBodyPart part)
{
DebugTools.AssertNotNull(Part);
DebugTools.AssertNotNull(part);
Owner.Transform.AttachParent(part.Owner);
foreach (var behavior in _behaviors.Values)
{
behavior.AddedToPart(part);
}
}
public void AddedToPartInBody(IBody body, IBodyPart part)
{
DebugTools.AssertNotNull(Body);
DebugTools.AssertNotNull(body);
DebugTools.AssertNotNull(Part);
DebugTools.AssertNotNull(part);
Owner.Transform.AttachParent(part.Owner);
foreach (var behavior in _behaviors.Values)
{
behavior.AddedToPartInBody(body, part);
}
}
public void RemovedFromBody(IBody old)
{
DebugTools.AssertNull(Body);
DebugTools.AssertNotNull(old);
foreach (var behavior in _behaviors.Values)
{
behavior.RemovedFromBody(old);
}
}
public void RemovedFromPart(IBodyPart old)
{
DebugTools.AssertNull(Part);
DebugTools.AssertNotNull(old);
Owner.Transform.AttachToGridOrMap();
foreach (var behavior in _behaviors.Values)
{
behavior.RemovedFromPart(old);
}
}
public void RemovedFromPartInBody(IBody oldBody, IBodyPart oldPart)
{
DebugTools.AssertNull(Body);
DebugTools.AssertNotNull(oldBody);
DebugTools.AssertNull(Part);
DebugTools.AssertNotNull(oldPart);
Owner.Transform.AttachToGridOrMap();
foreach (var behavior in _behaviors.Values)
{
behavior.RemovedFromPartInBody(oldBody, oldPart);
}
}
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Networks
{
public abstract class SharedBloodstreamComponent : Component
{
/// <summary>
/// Attempts to transfer the provided solution to an internal solution.
/// Only supports complete transfers.
/// </summary>
/// <param name="solution">The solution to be transferred.</param>
/// <returns>Whether or not transfer was successful.</returns>
public abstract bool TryTransferSolution(Solution solution);
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// Determines whether two <see cref="IBodyPart"/>s can connect.
/// </summary>
[Serializable, NetSerializable]
public enum BodyPartCompatibility
{
Universal = 0,
Biological,
Mechanical
}
}

View File

@@ -1,81 +0,0 @@
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Body.Part.Property;
namespace Content.Shared.GameObjects.Components.Body.Part
{
public static class BodyPartExtensions
{
/// <summary>
/// Checks if the given <see cref="IBodyPart"/> has the specified property.
/// </summary>
/// <param name="part">The <see cref="IBodyPart"/> to check in.</param>
/// <param name="type">
/// The type of <see cref="IBodyPartProperty"/> to check for.
/// </param>
/// <returns>true if found, false otherwise.</returns>
public static bool HasProperty(this IBodyPart part, Type type)
{
return part.Owner.HasComponent(type);
}
/// <summary>
/// Checks if the given <see cref="IBodyPart"/> has the specified property.
/// </summary>
/// <param name="part">The <see cref="IBodyPart"/> to check in.</param>
/// <typeparam name="T">
/// The type of <see cref="IBodyPartProperty"/> to check for.
/// </typeparam>
/// <returns>true if found, false otherwise.</returns>
public static bool HasProperty<T>(this IBodyPart part) where T : class, IBodyPartProperty
{
return part.HasProperty(typeof(T));
}
/// <summary>
/// Tries to retrieve the <see cref="IBodyPartProperty"/> with the
/// specified type.
/// </summary>
/// <param name="part">The <see cref="IBodyPart"/> to search in.</param>
/// <param name="type">
/// The type of <see cref="IBodyPartProperty"/> to search for.
/// </param>
/// <param name="property">
/// The property, if it was found. Null otherwise.
/// </param>
/// <returns>
/// true if a component with the specified type was found, false otherwise.
/// </returns>
public static bool TryGetProperty(this IBodyPart part, Type type,
[NotNullWhen(true)] out IBodyPartProperty? property)
{
if (!part.Owner.TryGetComponent(type, out var component))
{
property = null;
return false;
}
return (property = component as IBodyPartProperty) != null;
}
/// <summary>
/// Tries to retrieve the <see cref="IBodyPartProperty"/> with the
/// specified type.
/// </summary>
/// <param name="part">The <see cref="IBodyPart"/> to search in.</param>
/// <typeparam name="T">
/// The type of <see cref="IBodyPartProperty"/> to search for.
/// </typeparam>
/// <param name="property">
/// The property, if it was found. Null otherwise.
/// </param>
/// <returns>
/// true if a component with the specified type was found, false otherwise.
/// </returns>
public static bool TryGetProperty<T>(this IBodyPart part, [NotNullWhen(true)] out T? property) where T : class, IBodyPartProperty
{
return part.Owner.TryGetComponent(out property);
}
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// Defines the symmetry of a <see cref="IBodyPart"/>.
/// </summary>
[Serializable, NetSerializable]
public enum BodyPartSymmetry
{
None = 0,
Left,
Right
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// Defines the type of a <see cref="IBodyPart"/>.
/// </summary>
[Serializable, NetSerializable]
public enum BodyPartType
{
Other = 0,
Torso,
Head,
Arm,
Hand,
Leg,
Foot
}
}

View File

@@ -1,130 +0,0 @@
#nullable enable
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Surgery;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.GameObjects.Components.Body.Part
{
public interface IBodyPart : IComponent, IBodyPartContainer
{
/// <summary>
/// The <see cref="IBody"/> to which this <see cref="IBodyPart"/> is
/// attached to.
/// </summary>
IBody? Body { get; set; }
/// <summary>
/// The string to show when displaying this part's name to players.
/// </summary>
string DisplayName { get; }
/// <summary>
/// <see cref="BodyPartType"/> that this <see cref="IBodyPart"/> is considered
/// to be.
/// For example, <see cref="BodyPartType.Arm"/>.
/// </summary>
BodyPartType PartType { get; }
/// <summary>
/// Determines how many mechanisms can be fit inside this
/// <see cref="IBodyPart"/>.
/// </summary>
int Size { get; }
// TODO BODY Mechanisms occupying different parts at the body level
/// <summary>
/// Collection of all <see cref="IMechanism"/>s currently inside this
/// <see cref="IBodyPart"/>.
/// To add and remove from this list see <see cref="TryAddMechanism"/> and
/// <see cref="RemoveMechanism"/>
/// </summary>
IReadOnlyCollection<IMechanism> Mechanisms { get; }
/// <summary>
/// Whether or not the owning <see cref="Body"/> will die if all
/// <see cref="IBodyPart"/>s of this type are removed from it.
/// </summary>
public bool IsVital { get; }
/// <summary>
/// The symmetry of this <see cref="IBodyPart"/>.
/// </summary>
public BodyPartSymmetry Symmetry { get; }
/// <summary>
/// Checks if the given <see cref="SurgeryType"/> can be used on
/// the current state of this <see cref="IBodyPart"/>.
/// </summary>
/// <returns>True if it can be used, false otherwise.</returns>
bool SurgeryCheck(SurgeryType surgery);
/// <summary>
/// Attempts to perform surgery on this <see cref="IBodyPart"/> with the given
/// tool.
/// </summary>
/// <returns>True if successful, false if there was an error.</returns>
public bool AttemptSurgery(SurgeryType toolType, IBodyPartContainer target, ISurgeon surgeon,
IEntity performer);
/// <summary>
/// Checks if another <see cref="IBodyPart"/> can be connected to this one.
/// </summary>
/// <param name="part">The part to connect.</param>
/// <returns>True if it can be connected, false otherwise.</returns>
bool CanAttachPart(IBodyPart part);
/// <summary>
/// Checks if a <see cref="IMechanism"/> can be added on this
/// <see cref="IBodyPart"/>.
/// </summary>
/// <returns>True if it can be added, false otherwise.</returns>
bool CanAddMechanism(IMechanism mechanism);
/// <summary>
/// Tries to add a <see cref="IMechanism"/> to this body.
/// </summary>
/// <param name="mechanism">The mechanism to add.</param>
/// <param name="force">
/// Whether or not to check if the mechanism is compatible.
/// Passing true does not guarantee it to be added, for example if
/// it was already added before.
/// </param>
/// <returns>true if added, false otherwise even if it was already added.</returns>
bool TryAddMechanism(IMechanism mechanism, bool force = false);
/// <summary>
/// Tries to remove the given <see cref="mechanism"/> from this
/// <see cref="IBodyPart"/>.
/// </summary>
/// <param name="mechanism">The mechanism to remove.</param>
/// <returns>True if it was removed, false otherwise.</returns>
bool RemoveMechanism(IMechanism mechanism);
/// <summary>
/// Tries to remove the given <see cref="mechanism"/> from this
/// <see cref="IBodyPart"/> and drops it at the specified coordinates.
/// </summary>
/// <param name="mechanism">The mechanism to remove.</param>
/// <param name="dropAt">The coordinates to drop it at.</param>
/// <returns>True if it was removed, false otherwise.</returns>
bool RemoveMechanism(IMechanism mechanism, EntityCoordinates dropAt);
/// <summary>
/// Tries to destroy the given <see cref="IMechanism"/> from
/// this <see cref="IBodyPart"/>.
/// The mechanism won't be deleted if it is not in this body part.
/// </summary>
/// <returns>
/// True if the mechanism was in this body part and destroyed,
/// false otherwise.
/// </returns>
bool DeleteMechanism(IMechanism mechanism);
/// <summary>
/// Gibs the body part.
/// </summary>
void Gib();
}
}

View File

@@ -1,39 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// This interface gives components behavior when a body part
/// is added to their owning entity.
/// </summary>
public interface IBodyPartAdded : IComponent
{
/// <summary>
/// Called when a <see cref="IBodyPart"/> is added to the
/// entity owning this component.
/// </summary>
/// <param name="args">Information about the part that was added.</param>
void BodyPartAdded(BodyPartAddedEventArgs args);
}
public class BodyPartAddedEventArgs : EventArgs
{
public BodyPartAddedEventArgs(string slot, IBodyPart part)
{
Slot = slot;
Part = part;
}
/// <summary>
/// The slot that <see cref="Part"/> was added to.
/// </summary>
public string Slot { get; }
/// <summary>
/// The part that was added.
/// </summary>
public IBodyPart Part { get; }
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// Defines a component as being capable of containing parts.
/// Used during surgery.
/// </summary>
// TODO BODY Remove
public interface IBodyPartContainer
{
}
}

View File

@@ -1,38 +0,0 @@
#nullable enable
using System;
namespace Content.Shared.GameObjects.Components.Body.Part
{
/// <summary>
/// This interface gives components behavior when a body part
/// is removed from their owning entity.
/// </summary>
public interface IBodyPartRemoved
{
/// <summary>
/// Called when a <see cref="IBodyPart"/> is removed from the
/// entity owning this component.
/// </summary>
/// <param name="args">Information about the part that was removed.</param>
void BodyPartRemoved(BodyPartRemovedEventArgs args);
}
public class BodyPartRemovedEventArgs : EventArgs
{
public BodyPartRemovedEventArgs(string slot, IBodyPart part)
{
Slot = slot;
Part = part;
}
/// <summary>
/// The slot that <see cref="Part"/> was removed from.
/// </summary>
public string Slot { get; }
/// <summary>
/// The part that was removed.
/// </summary>
public IBodyPart Part { get; }
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Body.Part.Property
{
/// <summary>
/// Property attachable to a <see cref="IBodyPart"/>.
/// For example, this is used to define the speed capabilities of a leg.
/// The movement system will look for a <see cref="LegComponent"/> on all
/// <see cref="IBodyPart"/>.
/// </summary>
public abstract class BodyPartPropertyComponent : Component, IBodyPartProperty
{
/// <summary>
/// Whether this property is currently active.
/// </summary>
[DataField("active")]
public bool Active { get; set; } = true;
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Body.Part.Property
{
/// <summary>
/// Defines the length of a <see cref="IBodyPart"/>.
/// </summary>
[RegisterComponent]
public class ExtensionComponent : BodyPartPropertyComponent
{
public override string Name => "Extension";
/// <summary>
/// Current distance in tiles.
/// </summary>
[DataField("distance")]
public float Distance { get; set; } = 3f;
}
}

View File

@@ -1,16 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Part.Property
{
/// <summary>
/// Defines a <see cref="IBodyPart"/> as being able to grasp around an entity,
/// for example picking up an item.
/// </summary>
// TODO BODY Implement
[RegisterComponent]
public class GraspComponent : BodyPartPropertyComponent
{
public override string Name => "Grasp";
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Part.Property
{
/// <summary>
/// Defines a property for a <see cref="IBodyPart"/>.
/// </summary>
public interface IBodyPartProperty : IComponent
{
bool Active { get; set; }
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Body.Part.Property
{
/// <summary>
/// Defines the speed at which a <see cref="IBodyPart"/> can move.
/// </summary>
[RegisterComponent]
public class LegComponent : BodyPartPropertyComponent
{
public override string Name => "Leg";
/// <summary>
/// Speed in tiles per second.
/// </summary>
[DataField("speed")]
public float Speed { get; set; } = 2.6f;
}
}

View File

@@ -1,359 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Surgery;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body.Part
{
public abstract class SharedBodyPartComponent : Component, IBodyPart
{
public override string Name => "BodyPart";
public override uint? NetID => ContentNetIDs.BODY_PART;
private IBody? _body;
// TODO BODY Remove
[DataField("mechanisms")]
private List<string> _mechanismIds = new();
public IReadOnlyList<string> MechanismIds => _mechanismIds;
[ViewVariables]
private readonly HashSet<IMechanism> _mechanisms = new();
[ViewVariables]
public IBody? Body
{
get => _body;
set
{
if (_body == value)
{
return;
}
var old = _body;
_body = value;
if (old != null)
{
RemovedFromBody(old);
}
if (value != null)
{
AddedToBody(value);
}
}
}
[ViewVariables]
public string DisplayName => Name;
[ViewVariables]
[DataField("partType")]
public BodyPartType PartType { get; private set; } = BodyPartType.Other;
[ViewVariables] [DataField("size")] public int Size { get; private set; } = 1;
[ViewVariables] public int SizeUsed { get; private set; }
// TODO BODY size used
// TODO BODY surgerydata
/// <summary>
/// What types of BodyParts this <see cref="IBodyPart"/> can easily attach to.
/// For the most part, most limbs aren't universal and require extra work to
/// attach between types.
/// </summary>
[ViewVariables]
[DataField("compatibility")]
public BodyPartCompatibility Compatibility { get; private set; } = BodyPartCompatibility.Universal;
/// <summary>
/// Set of all <see cref="IMechanism"/> currently inside this
/// <see cref="IBodyPart"/>.
/// </summary>
[ViewVariables]
public IReadOnlyCollection<IMechanism> Mechanisms => _mechanisms;
// TODO BODY Replace with a simulation of organs
/// <summary>
/// Represents if body part is vital for creature.
/// If the last vital body part is removed creature dies
/// </summary>
[ViewVariables]
[DataField("vital")]
public bool IsVital { get; private set; } = false;
[ViewVariables]
[DataField("symmetry")]
public BodyPartSymmetry Symmetry { get; private set; } = BodyPartSymmetry.None;
[ViewVariables]
public ISurgeryData? SurgeryDataComponent => Owner.GetComponentOrNull<ISurgeryData>();
protected virtual void OnAddMechanism(IMechanism mechanism)
{
var prototypeId = mechanism.Owner.Prototype!.ID;
if (!_mechanismIds.Contains(prototypeId))
{
_mechanismIds.Add(prototypeId);
}
mechanism.Part = this;
SizeUsed += mechanism.Size;
Dirty();
}
protected virtual void OnRemoveMechanism(IMechanism mechanism)
{
_mechanismIds.Remove(mechanism.Owner.Prototype!.ID);
mechanism.Part = null;
SizeUsed -= mechanism.Size;
Dirty();
}
public override ComponentState GetComponentState(ICommonSession player)
{
var mechanismIds = new EntityUid[_mechanisms.Count];
var i = 0;
foreach (var mechanism in _mechanisms)
{
mechanismIds[i] = mechanism.Owner.Uid;
i++;
}
return new BodyPartComponentState(mechanismIds);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not BodyPartComponentState state)
{
return;
}
var newMechanisms = state.Mechanisms();
foreach (var mechanism in _mechanisms.ToArray())
{
if (!newMechanisms.Contains(mechanism))
{
RemoveMechanism(mechanism);
}
}
foreach (var mechanism in newMechanisms)
{
if (!_mechanisms.Contains(mechanism))
{
TryAddMechanism(mechanism, true);
}
}
}
public bool SurgeryCheck(SurgeryType surgery)
{
return SurgeryDataComponent?.CheckSurgery(surgery) ?? false;
}
/// <summary>
/// Attempts to perform surgery on this <see cref="IBodyPart"/> with the given
/// tool.
/// </summary>
/// <returns>True if successful, false if there was an error.</returns>
public bool AttemptSurgery(SurgeryType toolType, IBodyPartContainer target, ISurgeon surgeon, IEntity performer)
{
DebugTools.AssertNotNull(toolType);
DebugTools.AssertNotNull(target);
DebugTools.AssertNotNull(surgeon);
DebugTools.AssertNotNull(performer);
return SurgeryDataComponent?.PerformSurgery(toolType, target, surgeon, performer) ?? false;
}
public bool CanAttachPart(IBodyPart part)
{
DebugTools.AssertNotNull(part);
return SurgeryDataComponent?.CanAttachBodyPart(part) ?? false;
}
public virtual bool CanAddMechanism(IMechanism mechanism)
{
DebugTools.AssertNotNull(mechanism);
return SurgeryDataComponent != null &&
SizeUsed + mechanism.Size <= Size &&
SurgeryDataComponent.CanAddMechanism(mechanism);
}
/// <summary>
/// Tries to add a mechanism onto this body part.
/// </summary>
/// <param name="mechanism">The mechanism to try to add.</param>
/// <param name="force">
/// Whether or not to check if the mechanism can be added.
/// </param>
/// <returns>
/// True if successful, false if there was an error
/// (e.g. not enough room in <see cref="IBodyPart"/>).
/// Will return false even when forced if the mechanism is already
/// added in this <see cref="IBodyPart"/>.
/// </returns>
public bool TryAddMechanism(IMechanism mechanism, bool force = false)
{
DebugTools.AssertNotNull(mechanism);
if (!force && !CanAddMechanism(mechanism))
{
return false;
}
if (!_mechanisms.Add(mechanism))
{
return false;
}
OnAddMechanism(mechanism);
return true;
}
public bool RemoveMechanism(IMechanism mechanism)
{
DebugTools.AssertNotNull(mechanism);
if (!_mechanisms.Remove(mechanism))
{
return false;
}
OnRemoveMechanism(mechanism);
return true;
}
public bool RemoveMechanism(IMechanism mechanism, EntityCoordinates coordinates)
{
if (RemoveMechanism(mechanism))
{
mechanism.Owner.Transform.Coordinates = coordinates;
return true;
}
return false;
}
public bool DeleteMechanism(IMechanism mechanism)
{
DebugTools.AssertNotNull(mechanism);
if (!RemoveMechanism(mechanism))
{
return false;
}
mechanism.Owner.Delete();
return true;
}
private void AddedToBody(IBody body)
{
Owner.Transform.LocalRotation = 0;
Owner.Transform.AttachParent(body.Owner);
OnAddedToBody(body);
foreach (var mechanism in _mechanisms)
{
mechanism.AddedToBody(body);
}
}
private void RemovedFromBody(IBody old)
{
if (!Owner.Transform.Deleted)
{
Owner.Transform.AttachToGridOrMap();
}
OnRemovedFromBody(old);
foreach (var mechanism in _mechanisms)
{
mechanism.RemovedFromBody(old);
}
}
protected virtual void OnAddedToBody(IBody body) { }
protected virtual void OnRemovedFromBody(IBody old) { }
public virtual void Gib()
{
foreach (var mechanism in _mechanisms)
{
RemoveMechanism(mechanism);
}
}
}
[Serializable, NetSerializable]
public class BodyPartComponentState : ComponentState
{
[NonSerialized] private List<IMechanism>? _mechanisms;
public readonly EntityUid[] MechanismIds;
public BodyPartComponentState(EntityUid[] mechanismIds) : base(ContentNetIDs.BODY_PART)
{
MechanismIds = mechanismIds;
}
public List<IMechanism> Mechanisms(IEntityManager? entityManager = null)
{
if (_mechanisms != null)
{
return _mechanisms;
}
entityManager ??= IoCManager.Resolve<IEntityManager>();
var mechanisms = new List<IMechanism>(MechanismIds.Length);
foreach (var id in MechanismIds)
{
if (!entityManager.TryGetEntity(id, out var entity))
{
continue;
}
if (!entity.TryGetComponent(out IMechanism? mechanism))
{
continue;
}
mechanisms.Add(mechanism);
}
return _mechanisms = mechanisms;
}
}
}

View File

@@ -1,33 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body.Preset
{
/// <summary>
/// Defines the <see cref="IBodyPart"/>s used in a <see cref="IBody"/>.
/// </summary>
[Prototype("bodyPreset")]
[Serializable, NetSerializable]
public class BodyPresetPrototype : IPrototype
{
[ViewVariables]
[DataField("id", required: true)]
public string ID { get; } = default!;
[DataField("partIDs")]
private Dictionary<string, string> _partIDs = new();
[ViewVariables]
[DataField("name")]
public string Name { get; } = string.Empty;
[ViewVariables]
public Dictionary<string, string> PartIDs => new(_partIDs);
}
}

View File

@@ -1,29 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Scanner
{
public abstract class SharedBodyScannerComponent : Component
{
public override string Name => "BodyScanner";
}
[Serializable, NetSerializable]
public enum BodyScannerUiKey
{
Key
}
[Serializable, NetSerializable]
public class BodyScannerUIState : BoundUserInterfaceState
{
public readonly EntityUid Uid;
public BodyScannerUIState(EntityUid uid)
{
Uid = uid;
}
}
}

View File

@@ -1,709 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Body.Part;
using Content.Shared.GameObjects.Components.Body.Part.Property;
using Content.Shared.GameObjects.Components.Body.Preset;
using Content.Shared.GameObjects.Components.Body.Slot;
using Content.Shared.GameObjects.Components.Body.Template;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body
{
// TODO BODY Damage methods for collections of IDamageableComponents
public abstract class SharedBodyComponent : Component, IBody, ISerializationHooks
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override string Name => "Body";
public override uint? NetID => ContentNetIDs.BODY;
[ViewVariables]
[DataField("template", required: true)]
private string? TemplateId { get; } = default;
[ViewVariables]
[DataField("preset", required: true)]
private string? PresetId { get; } = default;
[ViewVariables]
public BodyTemplatePrototype? Template => TemplateId == null
? null
: _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
[ViewVariables]
public BodyPresetPrototype? Preset => PresetId == null
? null
: _prototypeManager.Index<BodyPresetPrototype>(PresetId);
[ViewVariables]
private Dictionary<string, BodyPartSlot> SlotIds { get; } = new();
[ViewVariables]
private Dictionary<IBodyPart, BodyPartSlot> SlotParts { get; } = new();
[ViewVariables]
public IEnumerable<BodyPartSlot> Slots => SlotIds.Values;
[ViewVariables]
public IEnumerable<KeyValuePair<IBodyPart, BodyPartSlot>> Parts => SlotParts;
[ViewVariables]
public IEnumerable<BodyPartSlot> EmptySlots => Slots.Where(slot => slot.Part == null);
public BodyPartSlot? CenterSlot =>
Template?.CenterSlot is { } centerSlot
? SlotIds.GetValueOrDefault(centerSlot)
: null;
public IBodyPart? CenterPart => CenterSlot?.Part;
public override void Initialize()
{
base.Initialize();
// TODO BODY BeforeDeserialization
// TODO BODY Move to template or somewhere else
if (TemplateId != null)
{
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
foreach (var (id, partType) in template.Slots)
{
SetSlot(id, partType);
}
foreach (var (slotId, connectionIds) in template.Connections)
{
var connections = connectionIds.Select(id => SlotIds[id]);
SlotIds[slotId].SetConnectionsInternal(connections);
}
}
CalculateSpeed();
}
public override void OnRemove()
{
foreach (var slot in SlotIds.Values)
{
slot.Shutdown();
}
base.OnRemove();
}
private BodyPartSlot SetSlot(string id, BodyPartType type)
{
var slot = new BodyPartSlot(id, type);
SlotIds[id] = slot;
slot.PartAdded += part => OnAddPart(slot, part);
slot.PartRemoved += part => OnRemovePart(slot, part);
return slot;
}
private Dictionary<BodyPartSlot, IBodyPart> GetHangingParts(BodyPartSlot from)
{
var hanging = new Dictionary<BodyPartSlot, IBodyPart>();
foreach (var connection in from.Connections)
{
if (connection.Part != null &&
!ConnectedToCenter(connection.Part))
{
hanging.Add(connection, connection.Part);
}
}
return hanging;
}
protected virtual bool CanAddPart(string slotId, IBodyPart part)
{
if (!SlotIds.TryGetValue(slotId, out var slot) ||
slot.CanAddPart(part))
{
return false;
}
return true;
}
protected virtual void OnAddPart(BodyPartSlot slot, IBodyPart part)
{
SlotParts[part] = slot;
part.Body = this;
var argsAdded = new BodyPartAddedEventArgs(slot.Id, part);
foreach (var component in Owner.GetAllComponents<IBodyPartAdded>().ToArray())
{
component.BodyPartAdded(argsAdded);
}
// TODO BODY Sort this duplicate out
OnBodyChanged();
}
protected virtual void OnRemovePart(BodyPartSlot slot, IBodyPart part)
{
SlotParts.Remove(part);
foreach (var connectedSlot in slot.Connections)
{
if (connectedSlot.Part != null &&
!ConnectedToCenter(connectedSlot.Part))
{
RemovePart(connectedSlot.Part);
}
}
part.Body = null;
var args = new BodyPartRemovedEventArgs(slot.Id, part);
foreach (var component in Owner.GetAllComponents<IBodyPartRemoved>())
{
component.BodyPartRemoved(args);
}
// creadth: fall down if no legs
if (part.PartType == BodyPartType.Leg &&
GetPartsOfType(BodyPartType.Leg).ToArray().Length == 0)
{
EntitySystem.Get<SharedStandingStateSystem>().Down(Owner);
}
// creadth: immediately kill entity if last vital part removed
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
{
if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0)
{
damageable.ChangeDamage(DamageType.Bloodloss, 300, true); // TODO BODY KILL
}
}
OnBodyChanged();
}
public bool TryAddPart(string slotId, IBodyPart part)
{
DebugTools.AssertNotNull(part);
DebugTools.AssertNotNull(slotId);
if (!CanAddPart(slotId, part))
{
return false;
}
return SlotIds.TryGetValue(slotId, out var slot) &&
slot.TryAddPart(part);
}
public void SetPart(string slotId, IBodyPart part)
{
if (!SlotIds.TryGetValue(slotId, out var slot))
{
slot = SetSlot(slotId, part.PartType);
SlotIds[slotId] = slot;
}
slot.SetPart(part);
}
public bool HasPart(string slotId)
{
DebugTools.AssertNotNull(slotId);
return SlotIds.TryGetValue(slotId, out var slot) &&
slot.Part != null;
}
public bool HasPart(IBodyPart part)
{
DebugTools.AssertNotNull(part);
return SlotParts.ContainsKey(part);
}
public bool RemovePart(IBodyPart part)
{
DebugTools.AssertNotNull(part);
return SlotParts.TryGetValue(part, out var slot) &&
slot.RemovePart();
}
public bool RemovePart(string slotId)
{
DebugTools.AssertNotNull(slotId);
return SlotIds.TryGetValue(slotId, out var slot) &&
slot.RemovePart();
}
public bool RemovePart(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slotId)
{
DebugTools.AssertNotNull(part);
if (!SlotParts.TryGetValue(part, out var slot))
{
slotId = null;
return false;
}
if (!slot.RemovePart())
{
slotId = null;
return false;
}
slotId = slot;
return true;
}
public bool TryDropPart(BodyPartSlot slot, [NotNullWhen(true)] out Dictionary<BodyPartSlot, IBodyPart>? dropped)
{
DebugTools.AssertNotNull(slot);
if (!SlotIds.TryGetValue(slot.Id, out var ownedSlot) ||
ownedSlot != slot ||
slot.Part == null)
{
dropped = null;
return false;
}
var oldPart = slot.Part;
dropped = GetHangingParts(slot);
if (!slot.RemovePart())
{
dropped = null;
return false;
}
dropped[slot] = oldPart;
return true;
}
public bool ConnectedToCenter(IBodyPart part)
{
return TryGetSlot(part, out var result) &&
ConnectedToCenterPartRecursion(result);
}
private bool ConnectedToCenterPartRecursion(BodyPartSlot slot, HashSet<BodyPartSlot>? searched = null)
{
searched ??= new HashSet<BodyPartSlot>();
if (Template?.CenterSlot == null)
{
return false;
}
if (slot.Part == CenterPart)
{
return true;
}
searched.Add(slot);
foreach (var connection in slot.Connections)
{
if (!searched.Contains(connection) &&
ConnectedToCenterPartRecursion(connection, searched))
{
return true;
}
}
return false;
}
public bool HasSlot(string slot)
{
return SlotIds.ContainsKey(slot);
}
public IEnumerable<IBodyPart> GetParts()
{
foreach (var slot in SlotIds.Values)
{
if (slot.Part != null)
{
yield return slot.Part;
}
}
}
public bool TryGetPart(string slotId, [NotNullWhen(true)] out IBodyPart? result)
{
result = null;
return SlotIds.TryGetValue(slotId, out var slot) &&
(result = slot.Part) != null;
}
public BodyPartSlot? GetSlot(string id)
{
return SlotIds.GetValueOrDefault(id);
}
public BodyPartSlot? GetSlot(IBodyPart part)
{
return SlotParts.GetValueOrDefault(part);
}
public bool TryGetSlot(string slotId, [NotNullWhen(true)] out BodyPartSlot? slot)
{
return (slot = GetSlot(slotId)) != null;
}
public bool TryGetSlot(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slot)
{
return (slot = GetSlot(part)) != null;
}
public bool TryGetPartConnections(string slotId, [NotNullWhen(true)] out List<IBodyPart>? connections)
{
if (!SlotIds.TryGetValue(slotId, out var slot))
{
connections = null;
return false;
}
connections = new List<IBodyPart>();
foreach (var connection in slot.Connections)
{
if (connection.Part != null)
{
connections.Add(connection.Part);
}
}
if (connections.Count <= 0)
{
connections = null;
return false;
}
return true;
}
public bool HasSlotOfType(BodyPartType type)
{
foreach (var _ in GetSlotsOfType(type))
{
return true;
}
return false;
}
public IEnumerable<BodyPartSlot> GetSlotsOfType(BodyPartType type)
{
foreach (var slot in SlotIds.Values)
{
if (slot.PartType == type)
{
yield return slot;
}
}
}
public bool HasPartOfType(BodyPartType type)
{
foreach (var _ in GetPartsOfType(type))
{
return true;
}
return false;
}
public IEnumerable<IBodyPart> GetPartsOfType(BodyPartType type)
{
foreach (var slot in GetSlotsOfType(type))
{
if (slot.Part != null)
{
yield return slot.Part;
}
}
}
public IEnumerable<(IBodyPart part, IBodyPartProperty property)> GetPartsWithProperty(Type type)
{
foreach (var slot in SlotIds.Values)
{
if (slot.Part != null && slot.Part.TryGetProperty(type, out var property))
{
yield return (slot.Part, property);
}
}
}
public IEnumerable<(IBodyPart part, T property)> GetPartsWithProperty<T>() where T : class, IBodyPartProperty
{
foreach (var part in SlotParts.Keys)
{
if (part.TryGetProperty<T>(out var property))
{
yield return (part, property);
}
}
}
private void CalculateSpeed()
{
if (!Owner.TryGetComponent(out MovementSpeedModifierComponent? playerMover))
{
return;
}
var legs = GetPartsWithProperty<LegComponent>().ToArray();
float speedSum = 0;
foreach (var leg in legs)
{
var footDistance = DistanceToNearestFoot(leg.part);
if (Math.Abs(footDistance - float.MinValue) <= 0.001f)
{
continue;
}
speedSum += leg.property.Speed * (1 + (float) Math.Log(footDistance, 1024.0));
}
if (speedSum <= 0.001f)
{
playerMover.BaseWalkSpeed = 0.8f;
playerMover.BaseSprintSpeed = 2.0f;
}
else
{
// Extra legs stack diminishingly.
playerMover.BaseWalkSpeed =
speedSum / (legs.Length - (float) Math.Log(legs.Length, 4.0));
playerMover.BaseSprintSpeed = playerMover.BaseWalkSpeed * 1.75f;
}
}
/// <summary>
/// Called when the layout of this body changes.
/// </summary>
private void OnBodyChanged()
{
// Calculate move speed based on this body.
if (Owner.HasComponent<MovementSpeedModifierComponent>())
{
CalculateSpeed();
}
Dirty();
}
/// <summary>
/// Returns the combined length of the distance to the nearest
/// <see cref="IBodyPart"/> that is a foot.
/// If you consider a <see cref="IBody"/> a node map, then it will
/// look for a foot node from the given node. It can only search
/// through <see cref="IBodyPart"/>s with an
/// <see cref="ExtensionComponent"/>.
/// </summary>
/// <returns>
/// The distance to the foot if found, <see cref="float.MinValue"/>
/// otherwise.
/// </returns>
public float DistanceToNearestFoot(IBodyPart source)
{
if (source.PartType == BodyPartType.Foot &&
source.TryGetProperty<ExtensionComponent>(out var extension))
{
return extension.Distance;
}
return LookForFootRecursion(source);
}
private float LookForFootRecursion(IBodyPart current, HashSet<BodyPartSlot>? searched = null)
{
searched ??= new HashSet<BodyPartSlot>();
if (!current.TryGetProperty<ExtensionComponent>(out var extProperty))
{
return float.MinValue;
}
// Get all connected parts if the current part has an extension property
if (!TryGetSlot(current, out var slot))
{
return float.MinValue;
}
// If a connected BodyPart is a foot, return this BodyPart's length.
foreach (var connection in slot.Connections)
{
if (connection.PartType == BodyPartType.Foot &&
!searched.Contains(connection))
{
return extProperty.Distance;
}
}
// Otherwise, get the recursion values of all connected BodyParts and
// store them in a list.
var distances = new List<float>();
foreach (var connection in slot.Connections)
{
if (connection.Part == null || !searched.Contains(connection))
{
continue;
}
var result = LookForFootRecursion(connection.Part, searched);
if (Math.Abs(result - float.MinValue) > 0.001f)
{
distances.Add(result);
}
}
// If one or more of the searches found a foot, return the smallest one
// and add this ones length.
if (distances.Count > 0)
{
return distances.Min<float>() + extProperty.Distance;
}
return float.MinValue;
}
// TODO BODY optimize this
public BodyPartSlot SlotAt(int index)
{
return SlotIds.Values.ElementAt(index);
}
public KeyValuePair<IBodyPart, BodyPartSlot> PartAt(int index)
{
return SlotParts.ElementAt(index);
}
public override ComponentState GetComponentState(ICommonSession player)
{
var parts = new (string slot, EntityUid partId)[SlotParts.Count];
var i = 0;
foreach (var (part, slot) in SlotParts)
{
parts[i] = (slot.Id, part.Owner.Uid);
i++;
}
return new BodyComponentState(parts);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not BodyComponentState state)
{
return;
}
var newParts = state.Parts();
foreach (var (oldPart, slot) in SlotParts)
{
if (!newParts.TryGetValue(slot.Id, out var newPart) ||
newPart != oldPart)
{
RemovePart(oldPart);
}
}
foreach (var (slotId, newPart) in newParts)
{
if (!SlotIds.TryGetValue(slotId, out var slot) ||
slot.Part != newPart)
{
SetPart(slotId, newPart);
}
}
}
public virtual void Gib(bool gibParts = false)
{
foreach (var part in SlotParts.Keys)
{
RemovePart(part);
if (gibParts)
part.Gib();
}
}
}
[Serializable, NetSerializable]
public class BodyComponentState : ComponentState
{
private Dictionary<string, IBodyPart>? _parts;
public readonly (string slot, EntityUid partId)[] PartIds;
public BodyComponentState((string slot, EntityUid partId)[] partIds) : base(ContentNetIDs.BODY)
{
PartIds = partIds;
}
public Dictionary<string, IBodyPart> Parts(IEntityManager? entityManager = null)
{
if (_parts != null)
{
return _parts;
}
entityManager ??= IoCManager.Resolve<IEntityManager>();
var parts = new Dictionary<string, IBodyPart>(PartIds.Length);
foreach (var (slot, partId) in PartIds)
{
if (!entityManager.TryGetEntity(partId, out var entity))
{
continue;
}
if (!entity.TryGetComponent(out IBodyPart? part))
{
continue;
}
parts[slot] = part;
}
return _parts = parts;
}
}
}

View File

@@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body.Slot
{
public class BodyPartSlot
{
public BodyPartSlot(string id, BodyPartType partType, IEnumerable<BodyPartSlot> connections)
{
Id = id;
PartType = partType;
Connections = new HashSet<BodyPartSlot>(connections);
}
public BodyPartSlot(string id, BodyPartType partType)
{
Id = id;
PartType = partType;
Connections = new HashSet<BodyPartSlot>();
}
/// <summary>
/// The ID of this slot.
/// </summary>
[ViewVariables]
public string Id { get; }
/// <summary>
/// The part type that this slot accepts.
/// </summary>
[ViewVariables]
public BodyPartType PartType { get; }
/// <summary>
/// The part currently in this slot, if any.
/// </summary>
[ViewVariables]
public IBodyPart? Part { get; private set; }
/// <summary>
/// List of slots that this slot connects to.
/// </summary>
[ViewVariables]
public HashSet<BodyPartSlot> Connections { get; private set; }
public event Action<IBodyPart>? PartAdded;
public event Action<IBodyPart>? PartRemoved;
internal void SetConnectionsInternal(IEnumerable<BodyPartSlot> connections)
{
Connections = new HashSet<BodyPartSlot>(connections);
}
public bool CanAddPart(IBodyPart part)
{
return Part == null && part.PartType == PartType;
}
public bool TryAddPart(IBodyPart part)
{
if (!CanAddPart(part))
{
return false;
}
SetPart(part);
return true;
}
public void SetPart(IBodyPart part)
{
if (Part != null)
{
RemovePart();
}
Part = part;
PartAdded?.Invoke(part);
}
public bool RemovePart()
{
if (Part == null)
{
return false;
}
var old = Part;
Part = null;
PartRemoved?.Invoke(old);
return true;
}
public void Shutdown()
{
Part = null;
Connections.Clear();
PartAdded = null;
PartRemoved = null;
}
}
}

View File

@@ -1,34 +0,0 @@
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Surgery
{
/// <summary>
/// Interface representing an entity capable of performing surgery,
/// such as a circular saw.
/// </summary>
public interface ISurgeon
{
public delegate void MechanismRequestCallback(
IMechanism target,
IBodyPartContainer container,
ISurgeon surgeon,
IEntity performer);
/// <summary>
/// How long it takes to perform a single surgery step in seconds.
/// </summary>
public float BaseOperationTime { get; set; }
/// <summary>
/// When performing a surgery, the <see cref="SurgeryDataComponent"/>
/// may sometimes require selecting from a set of
/// <see cref="IMechanism"/>s to operate on.
/// This function is called in that scenario, and it is expected that you call
/// the callback with one <see cref="IMechanism"/> from the provided list.
/// </summary>
public void RequestMechanism(IEnumerable<IMechanism> options, MechanismRequestCallback callback);
}
}

View File

@@ -1,84 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Body.Surgery
{
/// <summary>
/// Represents the current surgery state of a <see cref="IBodyPart"/>.
/// </summary>
public interface ISurgeryData : IComponent
{
public delegate void SurgeryAction(IBodyPartContainer container, ISurgeon surgeon, IEntity performer);
/// <summary>
/// The <see cref="IBodyPart"/> this
/// <see cref="ISurgeryData"/> is attached to.
/// </summary>
public IBodyPart? Parent { get; }
/// <summary>
/// The <see cref="BodyPartType"/> of the parent
/// <see cref="IBodyPart"/>.
/// </summary>
public BodyPartType? ParentType { get; }
/// <summary>
/// Returns a description of this entity.
/// </summary>
/// <returns>The description shown upon observing this entity.</returns>
public string GetDescription();
/// <summary>
/// Returns whether a <see cref="IMechanism"/> can be added into the
/// <see cref="IBodyPart"/> this <see cref="ISurgeryData"/>
/// represents.
/// </summary>
public bool CanAddMechanism(IMechanism mechanism);
/// <summary>
/// Returns whether the given <see cref="IBodyPart"/> can be connected
/// to the <see cref="IBodyPart"/> this <see cref="ISurgeryData"/>
/// represents.
/// </summary>
public bool CanAttachBodyPart(IBodyPart part);
/// <summary>
/// Gets the delegate corresponding to the surgery step using the given
/// <see cref="SurgeryType"/>.
/// </summary>
/// <returns>
/// The corresponding surgery action or null if no step can be
/// performed.
/// </returns>
public SurgeryAction? GetSurgeryStep(SurgeryType toolType);
/// <summary>
/// Returns whether the given <see cref="SurgeryType"/> can be used to
/// perform a surgery on the <see cref="IBodyPart"/> this
/// <see cref="ISurgeryData"/> represents.
/// </summary>
public bool CheckSurgery(SurgeryType toolType)
{
return GetSurgeryStep(toolType) != null;
}
/// <summary>
/// Attempts to perform surgery of the given <see cref="SurgeryType"/>.
/// </summary>
/// <param name="surgeryType">
/// The <see cref="SurgeryType"/> used for this surgery.
/// </param>
/// <param name="container">
/// The container where the surgery is being done.
/// </param>
/// <param name="surgeon">
/// The entity being used to perform the surgery.
/// </param>
/// <param name="performer">The entity performing the surgery.</param>
/// <returns>True if successful, false otherwise.</returns>
public bool PerformSurgery(SurgeryType surgeryType, IBodyPartContainer container, ISurgeon surgeon,
IEntity performer);
}
}

View File

@@ -1,22 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Surgery
{
/// <summary>
/// Types of surgery operations that can be performed.
/// </summary>
// TODO BODY Move this to YAML?
[Serializable, NetSerializable]
public enum SurgeryType
{
None = 0,
Incision,
Retraction,
Cauterization,
VesselCompression,
Drilling,
Amputation
}
}

View File

@@ -1,12 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Surgery
{
[Serializable, NetSerializable]
public enum SurgeryUIKey
{
Key
}
}

View File

@@ -1,74 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Body.Surgery
{
[Serializable, NetSerializable]
public class RequestBodyPartSurgeryUIMessage : BoundUserInterfaceMessage
{
public Dictionary<string, int> Targets;
public RequestBodyPartSurgeryUIMessage(Dictionary<string, int> targets)
{
Targets = targets;
}
}
[Serializable, NetSerializable]
public class RequestMechanismSurgeryUIMessage : BoundUserInterfaceMessage
{
public Dictionary<string, int> Targets;
public RequestMechanismSurgeryUIMessage(Dictionary<string, int> targets)
{
Targets = targets;
}
}
[Serializable, NetSerializable]
public class RequestBodyPartSlotSurgeryUIMessage : BoundUserInterfaceMessage
{
public Dictionary<string, int> Targets;
public RequestBodyPartSlotSurgeryUIMessage(Dictionary<string, int> targets)
{
Targets = targets;
}
}
[Serializable, NetSerializable]
public class ReceiveBodyPartSurgeryUIMessage : BoundUserInterfaceMessage
{
public int SelectedOptionId;
public ReceiveBodyPartSurgeryUIMessage(int selectedOptionId)
{
SelectedOptionId = selectedOptionId;
}
}
[Serializable, NetSerializable]
public class ReceiveMechanismSurgeryUIMessage : BoundUserInterfaceMessage
{
public int SelectedOptionId;
public ReceiveMechanismSurgeryUIMessage(int selectedOptionId)
{
SelectedOptionId = selectedOptionId;
}
}
[Serializable, NetSerializable]
public class ReceiveBodyPartSlotSurgeryUIMessage : BoundUserInterfaceMessage
{
public int SelectedOptionId;
public ReceiveBodyPartSlotSurgeryUIMessage(int selectedOptionId)
{
SelectedOptionId = selectedOptionId;
}
}
}

View File

@@ -1,91 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body.Template
{
/// <summary>
/// Defines the layout of a <see cref="IBody"/>.
/// </summary>
[Prototype("bodyTemplate")]
[Serializable, NetSerializable]
public class BodyTemplatePrototype : IPrototype, ISerializationHooks
{
[DataField("slots")]
private Dictionary<string, BodyPartType> _slots = new();
[DataField("connections")]
private Dictionary<string, List<string>> _rawConnections = new();
[DataField("layers")]
private Dictionary<string, string> _layers = new();
[DataField("mechanismLayers")]
private Dictionary<string, string> _mechanismLayers = new();
[ViewVariables]
[DataField("id", required: true)]
public string ID { get; } = default!;
[ViewVariables]
[DataField("name")]
public string Name { get; } = string.Empty;
[ViewVariables]
[DataField("centerSlot")]
public string CenterSlot { get; } = string.Empty;
[ViewVariables]
public Dictionary<string, BodyPartType> Slots => new(_slots);
[ViewVariables]
public Dictionary<string, HashSet<string>> Connections { get; set; } = new();
[ViewVariables]
public Dictionary<string, string> Layers => new(_layers);
[ViewVariables]
public Dictionary<string, string> MechanismLayers => new(_mechanismLayers);
void ISerializationHooks.AfterDeserialization()
{
//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.
var cleanedConnections = new Dictionary<string, HashSet<string>>();
foreach (var targetSlotName in _slots.Keys)
{
var tempConnections = new HashSet<string>();
foreach (var (slotName, slotConnections) in _rawConnections)
{
if (slotName == targetSlotName)
{
foreach (var connection in slotConnections)
{
if (!tempConnections.Contains(connection))
{
tempConnections.Add(connection);
}
}
}
else if (slotConnections.Contains(targetSlotName))
{
tempConnections.Add(slotName);
}
}
if (tempConnections.Count > 0)
{
cleanedConnections.Add(targetSlotName, tempConnections);
}
}
Connections = cleanedConnections;
}
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Botany
{
[Serializable, NetSerializable]
public enum PlantHolderVisuals
{
Plant,
HealthLight,
WaterLight,
NutritionLight,
AlertLight,
HarvestLight,
}
}

View File

@@ -1,148 +0,0 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Strap;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Buckle
{
public abstract class SharedBuckleComponent : Component, IActionBlocker, IEffectBlocker, IDraggable
{
public sealed override string Name => "Buckle";
public sealed override uint? NetID => ContentNetIDs.BUCKLE;
/// <summary>
/// The range from which this entity can buckle to a <see cref="SharedStrapComponent"/>.
/// </summary>
[ViewVariables]
[DataField("range")]
public float Range { get; protected set; } = SharedInteractionSystem.InteractionRange / 1.4f;
/// <summary>
/// True if the entity is buckled, false otherwise.
/// </summary>
public abstract bool Buckled { get; }
public EntityUid? LastEntityBuckledTo { get; set; }
public bool IsOnStrapEntityThisFrame { get; set; }
public bool DontCollide { get; set; }
public abstract bool TryBuckle(IEntity? user, IEntity to);
bool IActionBlocker.CanMove()
{
return !Buckled;
}
bool IActionBlocker.CanChangeDirection()
{
return !Buckled;
}
bool IEffectBlocker.CanFall()
{
return !Buckled;
}
bool IDraggable.CanDrop(CanDropEvent args)
{
return args.Target.HasComponent<SharedStrapComponent>();
}
bool IDraggable.Drop(DragDropEvent args)
{
return TryBuckle(args.User, args.Target);
}
}
[Serializable, NetSerializable]
public sealed class BuckleComponentState : ComponentState
{
public BuckleComponentState(bool buckled, int? drawDepth, EntityUid? lastEntityBuckledTo, bool dontCollide) : base(ContentNetIDs.BUCKLE)
{
Buckled = buckled;
DrawDepth = drawDepth;
LastEntityBuckledTo = lastEntityBuckledTo;
DontCollide = dontCollide;
}
public bool Buckled { get; }
public EntityUid? LastEntityBuckledTo { get; }
public bool DontCollide { get; }
public int? DrawDepth;
}
[Serializable, NetSerializable]
public enum BuckleVisuals
{
Buckled
}
[Serializable, NetSerializable]
public abstract class BuckleChangeMessage : ComponentMessage
{
/// <summary>
/// Constructs a new instance of <see cref="BuckleChangeMessage"/>
/// </summary>
/// <param name="entity">The entity that had its buckling status changed</param>
/// <param name="strap">The strap that the entity was buckled to or unbuckled from</param>
/// <param name="buckled">True if the entity was buckled, false otherwise</param>
protected BuckleChangeMessage(IEntity entity, IEntity strap, bool buckled)
{
Entity = entity;
Strap = strap;
Buckled = buckled;
}
/// <summary>
/// The entity that had its buckling status changed
/// </summary>
public IEntity Entity { get; }
/// <summary>
/// The strap that the entity was buckled to or unbuckled from
/// </summary>
public IEntity Strap { get; }
/// <summary>
/// True if the entity was buckled, false otherwise.
/// </summary>
public bool Buckled { get; }
}
[Serializable, NetSerializable]
public class BuckleMessage : BuckleChangeMessage
{
/// <summary>
/// Constructs a new instance of <see cref="BuckleMessage"/>
/// </summary>
/// <param name="entity">The entity that had its buckling status changed</param>
/// <param name="strap">The strap that the entity was buckled to or unbuckled from</param>
public BuckleMessage(IEntity entity, IEntity strap) : base(entity, strap, true)
{
}
}
[Serializable, NetSerializable]
public class UnbuckleMessage : BuckleChangeMessage
{
/// <summary>
/// Constructs a new instance of <see cref="UnbuckleMessage"/>
/// </summary>
/// <param name="entity">The entity that had its buckling status changed</param>
/// <param name="strap">The strap that the entity was buckled to or unbuckled from</param>
public UnbuckleMessage(IEntity entity, IEntity strap) : base(entity, strap, false)
{
}
}
}

View File

@@ -1,100 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Cargo
{
public class SharedCargoConsoleComponent : Component
{
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
public sealed override string Name => "CargoConsole";
/// <summary>
/// Sends away or requests shuttle
/// </summary>
[Serializable, NetSerializable]
public class CargoConsoleShuttleMessage : BoundUserInterfaceMessage
{
public CargoConsoleShuttleMessage()
{
}
}
/// <summary>
/// Add order to database.
/// </summary>
[Serializable, NetSerializable]
public class CargoConsoleAddOrderMessage : BoundUserInterfaceMessage
{
public string Requester;
public string Reason;
public string ProductId;
public int Amount;
public CargoConsoleAddOrderMessage(string requester, string reason, string productId, int amount)
{
Requester = requester;
Reason = reason;
ProductId = productId;
Amount = amount;
}
}
/// <summary>
/// Remove order from database.
/// </summary>
[Serializable, NetSerializable]
public class CargoConsoleRemoveOrderMessage : BoundUserInterfaceMessage
{
public int OrderNumber;
public CargoConsoleRemoveOrderMessage(int orderNumber)
{
OrderNumber = orderNumber;
}
}
/// <summary>
/// Set order in database as approved.
/// </summary>
[Serializable, NetSerializable]
public class CargoConsoleApproveOrderMessage : BoundUserInterfaceMessage
{
public int OrderNumber;
public CargoConsoleApproveOrderMessage(int orderNumber)
{
OrderNumber = orderNumber;
}
}
[NetSerializable, Serializable]
public enum CargoConsoleUiKey
{
Key
}
}
[NetSerializable, Serializable]
public class CargoConsoleInterfaceState : BoundUserInterfaceState
{
public readonly bool RequestOnly;
public readonly int BankId;
public readonly string BankName;
public readonly int BankBalance;
public readonly (int CurrentCapacity, int MaxCapacity) ShuttleCapacity;
public CargoConsoleInterfaceState(bool requestOnly, int bankId, string bankName, int bankBalance, (int CurrentCapacity, int MaxCapacity) shuttleCapacity)
{
RequestOnly = requestOnly;
BankId = bankId;
BankName = bankName;
BankBalance = bankBalance;
ShuttleCapacity = shuttleCapacity;
}
}
}

View File

@@ -1,26 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Prototypes.Cargo;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Cargo
{
public class SharedCargoOrderDatabaseComponent : Component
{
public sealed override string Name => "CargoOrderDatabase";
public sealed override uint? NetID => ContentNetIDs.CARGO_ORDER_DATABASE;
}
[NetSerializable, Serializable]
public class CargoOrderDatabaseState : ComponentState
{
public readonly List<CargoOrderData>? Orders;
public CargoOrderDatabaseState(List<CargoOrderData>? orders) : base(ContentNetIDs.CARGO_ORDER_DATABASE)
{
Orders = orders;
}
}
}

View File

@@ -1,106 +0,0 @@
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using Content.Shared.Prototypes.Cargo;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Cargo
{
public class SharedGalacticMarketComponent : Component, IEnumerable<CargoProductPrototype>, ISerializationHooks
{
public sealed override string Name => "GalacticMarket";
public sealed override uint? NetID => ContentNetIDs.GALACTIC_MARKET;
[DataField("products")]
protected List<string> _productIds = new();
protected readonly List<CargoProductPrototype> _products = new();
/// <summary>
/// A read-only list of products.
/// </summary>
public IReadOnlyList<CargoProductPrototype> Products => _products;
void ISerializationHooks.AfterDeserialization()
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
_products.Clear();
foreach (var id in _productIds)
{
if (!prototypeManager.TryIndex(id, out CargoProductPrototype? product))
{
continue;
}
_products.Add(product);
}
}
void ISerializationHooks.BeforeSerialization()
{
_productIds.Clear();
foreach (var product in _products)
{
_productIds.Add(product.ID);
}
}
public IEnumerator<CargoProductPrototype> GetEnumerator()
{
return _products.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Returns a product from the string id;
/// </summary>
/// <returns>Product</returns>
public CargoProductPrototype? GetProduct(string productId)
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
if (!prototypeManager.TryIndex(productId, out CargoProductPrototype? product) || !_products.Contains(product))
{
return null;
}
return product;
}
/// <summary>
/// Returns a list with the IDs of all products.
/// </summary>
/// <returns>A list of product IDs</returns>
public List<string> GetProductIdList()
{
var productIds = new List<string>();
foreach (var product in _products)
{
productIds.Add(product.ID);
}
return productIds;
}
}
[Serializable, NetSerializable]
public class GalacticMarketState : ComponentState
{
public List<string> Products;
public GalacticMarketState(List<string> technologies) : base(ContentNetIDs.GALACTIC_MARKET)
{
Products = technologies;
}
}
}

View File

@@ -1,116 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Observer;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry.ChemMaster
{
/// <summary>
/// Shared class for <c>ChemMasterComponent</c>. Provides a way for entities to split reagents from a beaker and produce pills and bottles via a user interface.
/// </summary>
public class SharedChemMasterComponent : Component
{
public override string Name => "ChemMaster";
[Serializable, NetSerializable]
public class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly bool HasPower;
public readonly bool HasBeaker;
public readonly ReagentUnit BeakerCurrentVolume;
public readonly ReagentUnit BeakerMaxVolume;
public readonly string ContainerName;
/// <summary>
/// A list of the reagents and their amounts within the beaker/reagent container, if applicable.
/// </summary>
public readonly IReadOnlyList<Solution.ReagentQuantity> ContainerReagents;
/// <summary>
/// A list of the reagents and their amounts within the buffer, if applicable.
/// </summary>
public readonly IReadOnlyList<Solution.ReagentQuantity> BufferReagents;
public readonly string DispenserName;
public readonly bool BufferModeTransfer;
public readonly ReagentUnit BufferCurrentVolume;
public ChemMasterBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
string dispenserName, IReadOnlyList<Solution.ReagentQuantity> containerReagents, IReadOnlyList<Solution.ReagentQuantity> bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume)
{
HasPower = hasPower;
HasBeaker = hasBeaker;
BeakerCurrentVolume = beakerCurrentVolume;
BeakerMaxVolume = beakerMaxVolume;
ContainerName = containerName;
DispenserName = dispenserName;
ContainerReagents = containerReagents;
BufferReagents = bufferReagents;
BufferModeTransfer = bufferModeTransfer;
BufferCurrentVolume = bufferCurrentVolume;
}
}
/// <summary>
/// Message data sent from client to server when a ChemMaster ui button is pressed.
/// </summary>
[Serializable, NetSerializable]
public class UiActionMessage : BoundUserInterfaceMessage
{
public readonly UiAction action;
public readonly ReagentUnit amount;
public readonly string id = "";
public readonly bool isBuffer;
public readonly int pillAmount;
public readonly int bottleAmount;
public UiActionMessage(UiAction _action, ReagentUnit? _amount, string? _id, bool? _isBuffer, int? _pillAmount, int? _bottleAmount)
{
action = _action;
if (action == UiAction.ChemButton)
{
amount = _amount.GetValueOrDefault();
if (_id == null)
{
id = "null";
}
else
{
id = _id;
}
isBuffer = _isBuffer.GetValueOrDefault();
}
else
{
pillAmount = _pillAmount.GetValueOrDefault();
bottleAmount = _bottleAmount.GetValueOrDefault();
}
}
}
[Serializable, NetSerializable]
public enum ChemMasterUiKey
{
Key
}
/// <summary>
/// Used in <see cref="AcceptCloningChoiceMessage"/> to specify which button was pressed.
/// </summary>
public enum UiAction
{
Eject,
Transfer,
Discard,
ChemButton,
CreatePills,
CreateBottles
}
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry
{
[Serializable, NetSerializable]
public enum FoamVisuals : byte
{
State,
Color
}
}

View File

@@ -1,90 +0,0 @@
#nullable enable
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Chemistry
{
/// <summary>
/// High-level solution transferring operations like "what happens when a syringe tries to inject this entity."
/// </summary>
/// <remarks>
/// This interface is most often implemented by using <see cref="SharedSolutionContainerComponent"/>
/// and setting the appropriate <see cref="SolutionContainerCaps"/>
/// </remarks>
public interface ISolutionInteractionsComponent : IComponent
{
//
// INJECTING
//
/// <summary>
/// Whether we CAN POTENTIALLY be injected with solutions by items like syringes.
/// </summary>
/// <remarks>
/// <para>
/// This should NOT change to communicate behavior like "the container is full".
/// Change <see cref="InjectSpaceAvailable"/> to 0 for that.
/// </para>
/// <para>
/// If refilling is allowed (<see cref="CanRefill"/>) you should also always allow injecting.
/// </para>
/// </remarks>
bool CanInject => false;
/// <summary>
/// The amount of solution space available for injecting.
/// </summary>
ReagentUnit InjectSpaceAvailable => ReagentUnit.Zero;
/// <summary>
/// Actually inject reagents.
/// </summary>
void Inject(Solution solution)
{
}
//
// DRAWING
//
bool CanDraw => false;
ReagentUnit DrawAvailable => ReagentUnit.Zero;
Solution Draw(ReagentUnit amount)
{
return new();
}
//
// REFILLING
//
bool CanRefill => false;
ReagentUnit RefillSpaceAvailable => ReagentUnit.Zero;
/// <summary>
/// The amount that will transfer if something is spilled on the container.
/// </summary>
ReagentUnit MaxSpillRefill => ReagentUnit.Zero;
void Refill(Solution solution)
{
}
//
// DRAINING
//
bool CanDrain => false;
ReagentUnit DrainAvailable => ReagentUnit.Zero;
Solution Drain(ReagentUnit amount)
{
return new();
}
}
}

View File

@@ -1,16 +0,0 @@
using System;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Chemistry
{
[RegisterComponent]
public class ReactiveComponent : Component
{
public override string Name => "Reactive";
[DataField("reactions", true, serverOnly:true)]
public ReagentEntityReaction[] Reactions { get; } = Array.Empty<ReagentEntityReaction>();
}
}

View File

@@ -1,29 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser
{
/// <summary>
/// Is simply a list of reagents defined in yaml. This can then be set as a
/// <see cref="ReagentDispenserComponent"/>s <c>pack</c> value (also in yaml),
/// to define which reagents it's able to dispense. Based off of how vending
/// machines define their inventory.
/// </summary>
[Serializable, NetSerializable, Prototype("reagentDispenserInventory")]
public class ReagentDispenserInventoryPrototype : IPrototype
{
[DataField("inventory")]
private List<string> _inventory = new();
[ViewVariables]
[DataField("id", required: true)]
public string ID { get; } = default!;
public List<string> Inventory => _inventory;
}
}

View File

@@ -1,118 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser
{
/// <summary>
/// Shared class for <c>ReagentDispenserComponent</c>. Provides a way for entities to dispense and remove reagents from other entities with SolutionComponents via a user interface.
/// <para>This is useful for machines such as the chemical dispensers, booze dispensers, or soda dispensers.</para>
/// <para>The chemicals which may be dispensed are defined by specifying a reagent pack. See <see cref="ReagentDispenserInventoryPrototype"/> for more information on that.</para>
/// </summary>
public class SharedReagentDispenserComponent : Component
{
public override string Name => "ReagentDispenser";
/// <summary>
/// A list of reagents which this may dispense. Defined in yaml prototype, see <see cref="ReagentDispenserInventoryPrototype"/>.
/// </summary>
protected readonly List<ReagentDispenserInventoryEntry> Inventory = new();
[Serializable, NetSerializable]
public class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly bool HasPower;
public readonly bool HasBeaker;
public readonly ReagentUnit BeakerCurrentVolume;
public readonly ReagentUnit BeakerMaxVolume;
public readonly string ContainerName;
/// <summary>
/// A list of the reagents which this dispenser can dispense.
/// </summary>
public readonly List<ReagentDispenserInventoryEntry> Inventory;
/// <summary>
/// A list of the reagents and their amounts within the beaker/reagent container, if applicable.
/// </summary>
public readonly List<Solution.ReagentQuantity>? ContainerReagents;
public readonly string DispenserName;
public readonly ReagentUnit SelectedDispenseAmount;
public ReagentDispenserBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
List<ReagentDispenserInventoryEntry> inventory, string dispenserName, List<Solution.ReagentQuantity>? containerReagents, ReagentUnit selectedDispenseAmount)
{
HasPower = hasPower;
HasBeaker = hasBeaker;
BeakerCurrentVolume = beakerCurrentVolume;
BeakerMaxVolume = beakerMaxVolume;
ContainerName = containerName;
Inventory = inventory;
DispenserName = dispenserName;
ContainerReagents = containerReagents;
SelectedDispenseAmount = selectedDispenseAmount;
}
}
/// <summary>
/// Message data sent from client to server when a dispenser ui button is pressed.
/// </summary>
[Serializable, NetSerializable]
public class UiButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public readonly int DispenseIndex; //Index of dispense button / reagent being pressed. Only used when a dispense button is pressed.
public UiButtonPressedMessage(UiButton button, int dispenseIndex)
{
Button = button;
DispenseIndex = dispenseIndex;
}
}
[Serializable, NetSerializable]
public enum ReagentDispenserUiKey
{
Key
}
/// <summary>
/// Used in <see cref="UiButtonPressedMessage"/> to specify which button was pressed.
/// </summary>
public enum UiButton
{
Eject,
Clear,
SetDispenseAmount1,
SetDispenseAmount5,
SetDispenseAmount10,
SetDispenseAmount15,
SetDispenseAmount20,
SetDispenseAmount25,
SetDispenseAmount30,
SetDispenseAmount50,
SetDispenseAmount100,
/// <summary>
/// Used when any dispense button is pressed. Such as "Carbon", or "Oxygen" buttons on the chem dispenser.
/// The index of the reagent attached to that dispense button is sent as <see cref="UiButtonPressedMessage.DispenseIndex"/>.
/// </summary>
Dispense
}
/// <summary>
/// Information about a reagent which the dispenser can dispense.
/// </summary>
[Serializable, NetSerializable]
public struct ReagentDispenserInventoryEntry
{
public readonly string ID;
public ReagentDispenserInventoryEntry(string id)
{
ID = id;
}
}
}
}

View File

@@ -1,27 +0,0 @@
#nullable enable
using System;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry
{
public abstract class SharedHyposprayComponent : Component
{
public sealed override string Name => "Hypospray";
public sealed override uint? NetID => ContentNetIDs.HYPOSPRAY;
[Serializable, NetSerializable]
protected sealed class HyposprayComponentState : ComponentState
{
public ReagentUnit CurVolume { get; }
public ReagentUnit MaxVolume { get; }
public HyposprayComponentState(ReagentUnit curVolume, ReagentUnit maxVolume) : base(ContentNetIDs.HYPOSPRAY)
{
CurVolume = curVolume;
MaxVolume = maxVolume;
}
}
}
}

View File

@@ -1,41 +0,0 @@
#nullable enable
using System;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry
{
/// <summary>
/// Shared class for injectors & syringes
/// </summary>
public class SharedInjectorComponent : Component
{
public override string Name => "Injector";
public sealed override uint? NetID => ContentNetIDs.REAGENT_INJECTOR;
/// <summary>
/// Component data used for net updates. Used by client for item status ui
/// </summary>
[Serializable, NetSerializable]
protected sealed class InjectorComponentState : ComponentState
{
public ReagentUnit CurrentVolume { get; }
public ReagentUnit TotalVolume { get; }
public InjectorToggleMode CurrentMode { get; }
public InjectorComponentState(ReagentUnit currentVolume, ReagentUnit totalVolume, InjectorToggleMode currentMode) : base(ContentNetIDs.REAGENT_INJECTOR)
{
CurrentVolume = currentVolume;
TotalVolume = totalVolume;
CurrentMode = currentMode;
}
}
public enum InjectorToggleMode
{
Inject,
Draw
}
}
}

View File

@@ -1,326 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Chemistry
{
/// <summary>
/// Holds a <see cref="Solution"/> with a limited volume.
/// </summary>
public abstract class SharedSolutionContainerComponent : Component, IExamine, ISolutionInteractionsComponent
{
public override string Name => "SolutionContainer";
/// <inheritdoc />
public sealed override uint? NetID => ContentNetIDs.SOLUTION;
[ViewVariables]
[DataField("contents")]
public Solution Solution { get; private set; } = new();
public IReadOnlyList<Solution.ReagentQuantity> ReagentList => Solution.Contents;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("maxVol")]
public ReagentUnit MaxVolume { get; set; } = ReagentUnit.Zero;
[ViewVariables]
public ReagentUnit CurrentVolume => Solution.TotalVolume;
/// <summary>
/// Volume needed to fill this container.
/// </summary>
[ViewVariables]
public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume;
[ViewVariables]
public virtual Color Color => Solution.Color;
/// <summary>
/// If reactions will be checked for when adding reagents to the container.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("canReact")]
public bool CanReact { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("caps")]
public SolutionContainerCaps Capabilities { get; set; }
public bool CanExamineContents => Capabilities.HasCap(SolutionContainerCaps.CanExamine);
public bool CanUseWithChemDispenser => Capabilities.HasCap(SolutionContainerCaps.FitsInDispenser);
public bool CanInject => Capabilities.HasCap(SolutionContainerCaps.Injectable) || CanRefill;
public bool CanDraw => Capabilities.HasCap(SolutionContainerCaps.Drawable) || CanDrain;
public bool CanRefill => Capabilities.HasCap(SolutionContainerCaps.Refillable);
public bool CanDrain => Capabilities.HasCap(SolutionContainerCaps.Drainable);
public override void Initialize()
{
base.Initialize();
UpdateAppearance();
}
public void RemoveAllSolution()
{
if (CurrentVolume == 0)
return;
Solution.RemoveAllSolution();
ChemicalsRemoved();
}
/// <summary>
/// Adds reagent of an Id to the container.
/// </summary>
/// <param name="reagentId">The Id of the reagent to add.</param>
/// <param name="quantity">The amount of reagent to add.</param>
/// <param name="acceptedQuantity">The amount of reagent sucesfully added.</param>
/// <returns>If all the reagent could be added.</returns>
public bool TryAddReagent(string reagentId, ReagentUnit quantity, out ReagentUnit acceptedQuantity)
{
acceptedQuantity = EmptyVolume > quantity ? quantity : EmptyVolume;
Solution.AddReagent(reagentId, acceptedQuantity);
if (acceptedQuantity > 0)
ChemicalsAdded();
return acceptedQuantity == quantity;
}
/// <summary>
/// Removes reagent of an Id to the container.
/// </summary>
/// <param name="reagentId">The Id of the reagent to remove.</param>
/// <param name="quantity">The amount of reagent to remove.</param>
/// <returns>If the reagent to remove was found in the container.</returns>
public bool TryRemoveReagent(string reagentId, ReagentUnit quantity)
{
if (!Solution.ContainsReagent(reagentId))
return false;
Solution.RemoveReagent(reagentId, quantity);
ChemicalsRemoved();
return true;
}
/// <summary>
/// Removes part of the solution in the container.
/// </summary>
/// <param name="quantity">the volume of solution to remove.</param>
/// <returns>The solution that was removed.</returns>
public Solution SplitSolution(ReagentUnit quantity)
{
var splitSol = Solution.SplitSolution(quantity);
ChemicalsRemoved();
return splitSol;
}
/// <summary>
/// Checks if a solution can fit into the container.
/// </summary>
/// <param name="solution">The solution that is trying to be added.</param>
/// <returns>If the solution can be fully added.</returns>
public bool CanAddSolution(Solution solution)
{
return solution.TotalVolume <= EmptyVolume;
}
/// <summary>
/// Adds a solution to the container, if it can fully fit.
/// </summary>
/// <param name="solution">The solution to try to add.</param>
/// <returns>If the solution could be added.</returns>
public bool TryAddSolution(Solution solution)
{
if (!CanAddSolution(solution) || solution.TotalVolume == 0)
return false;
Solution.AddSolution(solution);
ChemicalsAdded();
return true;
}
private void ChemicalsAdded()
{
ProcessReactions();
SolutionChanged();
UpdateAppearance();
Dirty();
}
private void ChemicalsRemoved()
{
SolutionChanged();
UpdateAppearance();
Dirty();
}
private void SolutionChanged()
{
EntitySystem.Get<ChemistrySystem>().HandleSolutionChange(Owner);
}
private void ProcessReactions()
{
if (!CanReact)
return;
EntitySystem.Get<SharedChemicalReactionSystem>()
.FullyReactSolution(Solution, Owner, MaxVolume);
}
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
{
if (!CanExamineContents)
return;
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
if (ReagentList.Count == 0)
{
message.AddText(Loc.GetString("Contains no chemicals."));
return;
}
var primaryReagent = Solution.GetPrimaryReagentId();
if (!prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto))
{
Logger.Error($"{nameof(SharedSolutionContainerComponent)} could not find the prototype associated with {primaryReagent}.");
return;
}
var colorHex = Color.ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
var messageString = "It contains a [color={0}]{1}[/color] " + (ReagentList.Count == 1 ? "chemical." : "mixture of chemicals.");
message.AddMarkup(Loc.GetString(messageString, colorHex, Loc.GetString(proto.PhysicalDescription)));
}
ReagentUnit ISolutionInteractionsComponent.RefillSpaceAvailable => EmptyVolume;
ReagentUnit ISolutionInteractionsComponent.InjectSpaceAvailable => EmptyVolume;
ReagentUnit ISolutionInteractionsComponent.DrawAvailable => CurrentVolume;
ReagentUnit ISolutionInteractionsComponent.DrainAvailable => CurrentVolume;
[DataField("maxSpillRefill")]
public ReagentUnit MaxSpillRefill { get; set; }
void ISolutionInteractionsComponent.Refill(Solution solution)
{
if (!CanRefill)
return;
TryAddSolution(solution);
}
void ISolutionInteractionsComponent.Inject(Solution solution)
{
if (!CanInject)
return;
TryAddSolution(solution);
}
Solution ISolutionInteractionsComponent.Draw(ReagentUnit amount)
{
if (!CanDraw)
return new Solution();
return SplitSolution(amount);
}
Solution ISolutionInteractionsComponent.Drain(ReagentUnit amount)
{
if (!CanDrain)
return new Solution();
return SplitSolution(amount);
}
private void UpdateAppearance()
{
if (Owner.Deleted || !Owner.TryGetComponent<SharedAppearanceComponent>(out var appearance))
return;
appearance.SetData(SolutionContainerVisuals.VisualState, GetVisualState());
}
private SolutionContainerVisualState GetVisualState()
{
var filledVolumeFraction = CurrentVolume.Float() / MaxVolume.Float();
return new SolutionContainerVisualState(Color, filledVolumeFraction);
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new SolutionContainerComponentState(Solution);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState is not SolutionContainerComponentState containerState)
return;
Solution = containerState.Solution;
}
}
[Serializable, NetSerializable]
public enum SolutionContainerVisuals : byte
{
VisualState
}
[Serializable, NetSerializable]
public class SolutionContainerVisualState
{
public readonly Color Color;
/// <summary>
/// Represents how full the container is, as a fraction equivalent to <see cref="FilledVolumeFraction"/>/<see cref="byte.MaxValue"/>.
/// </summary>
public readonly byte FilledVolumeFraction;
// do we really need this just to save three bytes?
public float FilledVolumePercent => (float) FilledVolumeFraction / byte.MaxValue;
/// <param name="filledVolumeFraction">The fraction of the container's volume that is filled.</param>
public SolutionContainerVisualState(Color color, float filledVolumeFraction)
{
Color = color;
FilledVolumeFraction = (byte) (byte.MaxValue * filledVolumeFraction);
}
}
public enum SolutionContainerLayers : byte
{
Fill,
Base
}
[Serializable, NetSerializable]
public class SolutionContainerComponentState : ComponentState
{
public readonly Solution Solution;
public SolutionContainerComponentState(Solution solution) : base(ContentNetIDs.SOLUTION)
{
Solution = solution;
}
}
}

View File

@@ -1,12 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Chemistry
{
[Serializable, NetSerializable]
public enum SmokeVisuals : byte
{
Color
}
}

View File

@@ -1,56 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Command
{
public class SharedCommunicationsConsoleComponent : Component
{
public override string Name => "CommunicationsConsole";
}
[Serializable, NetSerializable]
public class CommunicationsConsoleInterfaceState : BoundUserInterfaceState
{
public readonly bool CanAnnounce;
public readonly bool CanCall;
public readonly TimeSpan? ExpectedCountdownEnd;
public readonly bool CountdownStarted;
public CommunicationsConsoleInterfaceState(bool canAnnounce, bool canCall, TimeSpan? expectedCountdownEnd = null)
{
CanAnnounce = canAnnounce;
CanCall = canCall;
ExpectedCountdownEnd = expectedCountdownEnd;
CountdownStarted = expectedCountdownEnd != null;
}
}
[Serializable, NetSerializable]
public class CommunicationsConsoleAnnounceMessage : BoundUserInterfaceMessage
{
public readonly string Message;
public CommunicationsConsoleAnnounceMessage(string message)
{
Message = message;
}
}
[Serializable, NetSerializable]
public class CommunicationsConsoleCallEmergencyShuttleMessage : BoundUserInterfaceMessage
{
}
[Serializable, NetSerializable]
public class CommunicationsConsoleRecallEmergencyShuttleMessage : BoundUserInterfaceMessage
{
}
[Serializable, NetSerializable]
public enum CommunicationsConsoleUiKey
{
Key
}
}

View File

@@ -1,11 +0,0 @@
#nullable enable
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Construction
{
[NetSerializable]
public enum MachineFrameVisuals
{
State,
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Verbs;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Context
{
[RegisterComponent]
public class HideContextMenuComponent : Component, IShowContextMenu
{
public override string Name => "HideContextMenu";
public bool ShowContextMenu(IEntity examiner)
{
return false;
}
}
}

View File

@@ -1,20 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Conveyor
{
[Serializable, NetSerializable]
public enum ConveyorVisuals
{
State
}
[Serializable, NetSerializable]
public enum ConveyorState
{
Off,
Forward,
Reversed
}
}

View File

@@ -1,34 +0,0 @@
#nullable enable
using Content.Shared.Damage;
namespace Content.Shared.GameObjects.Components.Damage
{
/// <summary>
/// Data class with information on how the value of a
/// single <see cref="DamageType"/> has changed.
/// </summary>
public struct DamageChangeData
{
/// <summary>
/// Type of damage that changed.
/// </summary>
public DamageType Type;
/// <summary>
/// The new current value for that damage.
/// </summary>
public int NewValue;
/// <summary>
/// How much the health value changed from its last value (negative is heals, positive is damage).
/// </summary>
public int Delta;
public DamageChangeData(DamageType type, int newValue, int delta)
{
Type = type;
NewValue = newValue;
Delta = delta;
}
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Body;
namespace Content.Shared.GameObjects.Components.Damage
{
/// <summary>
/// Data class with information on how to damage a
/// <see cref="IDamageableComponent"/>.
/// While not necessary to damage for all instances, classes such as
/// <see cref="SharedBodyComponent"/> may require it for extra data
/// (such as selecting which limb to target).
/// </summary>
public class DamageChangeParams : EventArgs
{
}
}

View File

@@ -1,36 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Damage;
namespace Content.Shared.GameObjects.Components.Damage
{
public class DamageChangedEventArgs : EventArgs
{
public DamageChangedEventArgs(IDamageableComponent damageable, IReadOnlyList<DamageChangeData> data)
{
Damageable = damageable;
Data = data;
}
public DamageChangedEventArgs(IDamageableComponent damageable, DamageType type, int newValue, int delta)
{
Damageable = damageable;
var datum = new DamageChangeData(type, newValue, delta);
var data = new List<DamageChangeData> {datum};
Data = data;
}
/// <summary>
/// Reference to the <see cref="IDamageableComponent"/> that invoked the event.
/// </summary>
public IDamageableComponent Damageable { get; }
/// <summary>
/// List containing data on each <see cref="DamageType"/> that was changed.
/// </summary>
public IReadOnlyList<DamageChangeData> Data { get; }
}
}

View File

@@ -1,52 +0,0 @@
#nullable enable
using System.Collections.Generic;
using Content.Shared.Damage;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Damage
{
public class DamageChangedMessage : ComponentMessage
{
public DamageChangedMessage(IDamageableComponent damageable, IReadOnlyList<DamageChangeData> data)
{
Damageable = damageable;
Data = data;
}
public DamageChangedMessage(IDamageableComponent damageable, DamageType type, int newValue, int delta)
{
Damageable = damageable;
var datum = new DamageChangeData(type, newValue, delta);
var data = new List<DamageChangeData> {datum};
Data = data;
}
/// <summary>
/// Reference to the <see cref="IDamageableComponent"/> that invoked the event.
/// </summary>
public IDamageableComponent Damageable { get; }
/// <summary>
/// List containing data on each <see cref="DamageType"/> that was changed.
/// </summary>
public IReadOnlyList<DamageChangeData> Data { get; }
public bool TookDamage
{
get
{
foreach (var datum in Data)
{
if (datum.Delta > 0)
{
return true;
}
}
return false;
}
}
}
}

View File

@@ -1,14 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Damage
{
[Flags]
[Serializable, NetSerializable]
public enum DamageFlag
{
None = 0,
Invulnerable = 1 << 0
}
}

View File

@@ -1,468 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.DamageContainer;
using Content.Shared.Damage.ResistanceSet;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Damage
{
/// <summary>
/// Component that allows attached entities to take damage.
/// This basic version never dies (thus can take an indefinite amount of damage).
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IDamageableComponent))]
public class DamageableComponent : Component, IDamageableComponent, IRadiationAct, ISerializationHooks
{
public override string Name => "Damageable";
public override uint? NetID => ContentNetIDs.DAMAGEABLE;
// TODO define these in yaml?
public const string DefaultResistanceSet = "defaultResistances";
public const string DefaultDamageContainer = "metallicDamageContainer";
private readonly Dictionary<DamageType, int> _damageList = DamageTypeExtensions.ToNewDictionary();
private readonly HashSet<DamageType> _supportedTypes = new();
private readonly HashSet<DamageClass> _supportedClasses = new();
[DataField("flags")]
private DamageFlag _flags;
[DataField("resistances")] public string ResistanceSetId = DefaultResistanceSet;
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
[ViewVariables] [DataField("damageContainer")] public string DamageContainerId { get; set; } = DefaultDamageContainer;
[ViewVariables] private ResistanceSet Resistances { get; set; } = new();
// TODO DAMAGE Cache this
[ViewVariables] public int TotalDamage => _damageList.Values.Sum();
[ViewVariables]
public IReadOnlyDictionary<DamageClass, int> DamageClasses =>
DamageTypeExtensions.ToClassDictionary(_damageList);
[ViewVariables] public IReadOnlyDictionary<DamageType, int> DamageTypes => _damageList;
public DamageFlag Flags
{
get => _flags;
private set
{
if (_flags == value)
{
return;
}
_flags = value;
Dirty();
}
}
public void AddFlag(DamageFlag flag)
{
Flags |= flag;
}
public bool HasFlag(DamageFlag flag)
{
return Flags.HasFlag(flag);
}
public void RemoveFlag(DamageFlag flag)
{
Flags &= ~flag;
}
public bool SupportsDamageClass(DamageClass @class)
{
return _supportedClasses.Contains(@class);
}
public bool SupportsDamageType(DamageType type)
{
return _supportedTypes.Contains(type);
}
public override void Initialize()
{
base.Initialize();
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
// TODO DAMAGE Serialize damage done and resistance changes
var damagePrototype = prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
_supportedClasses.Clear();
_supportedTypes.Clear();
DamageContainerId = damagePrototype.ID;
_supportedClasses.UnionWith(damagePrototype.SupportedClasses);
_supportedTypes.UnionWith(damagePrototype.SupportedTypes);
var resistancePrototype = prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId);
Resistances = new ResistanceSet(resistancePrototype);
}
protected override void Startup()
{
base.Startup();
ForceHealthChangedEvent();
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new DamageableComponentState(_damageList, _flags);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is DamageableComponentState state))
{
return;
}
_damageList.Clear();
foreach (var (type, damage) in state.DamageList)
{
_damageList[type] = damage;
}
_flags = state.Flags;
}
public int GetDamage(DamageType type)
{
return _damageList.GetValueOrDefault(type);
}
public bool TryGetDamage(DamageType type, out int damage)
{
return _damageList.TryGetValue(type, out damage);
}
public int GetDamage(DamageClass @class)
{
if (!SupportsDamageClass(@class))
{
return 0;
}
var damage = 0;
foreach (var type in @class.ToTypes())
{
damage += GetDamage(type);
}
return damage;
}
public bool TryGetDamage(DamageClass @class, out int damage)
{
if (!SupportsDamageClass(@class))
{
damage = 0;
return false;
}
damage = GetDamage(@class);
return true;
}
/// <summary>
/// Attempts to set the damage value for the given <see cref="DamageType"/>.
/// </summary>
/// <returns>
/// True if successful, false if this container does not support that type.
/// </returns>
public bool TrySetDamage(DamageType type, int newValue)
{
if (newValue < 0)
{
return false;
}
var damageClass = type.ToClass();
if (_supportedClasses.Contains(damageClass))
{
var old = _damageList[type] = newValue;
_damageList[type] = newValue;
var delta = newValue - old;
var datum = new DamageChangeData(type, newValue, delta);
var data = new List<DamageChangeData> {datum};
OnHealthChanged(data);
return true;
}
return false;
}
public void Heal(DamageType type)
{
SetDamage(type, 0);
}
public void Heal()
{
foreach (var type in _supportedTypes)
{
Heal(type);
}
}
public bool ChangeDamage(
DamageType type,
int amount,
bool ignoreResistances,
IEntity? source = null,
DamageChangeParams? extraParams = null)
{
if (amount > 0 && HasFlag(DamageFlag.Invulnerable))
{
return false;
}
if (!SupportsDamageType(type))
{
return false;
}
var finalDamage = amount;
if (!ignoreResistances)
{
finalDamage = Resistances.CalculateDamage(type, amount);
}
if (finalDamage == 0)
return false;
if (!_damageList.TryGetValue(type, out var current))
{
return false;
}
if (current + finalDamage < 0)
{
if (current == 0)
return false;
_damageList[type] = 0;
finalDamage = -current;
}
else
{
_damageList[type] = current + finalDamage;
}
current = _damageList[type];
var datum = new DamageChangeData(type, current, finalDamage);
var data = new List<DamageChangeData> {datum};
OnHealthChanged(data);
return true;
}
public bool ChangeDamage(DamageClass @class, int amount, bool ignoreResistances,
IEntity? source = null,
DamageChangeParams? extraParams = null)
{
if (amount > 0 && HasFlag(DamageFlag.Invulnerable))
{
return false;
}
if (!SupportsDamageClass(@class))
{
return false;
}
var types = @class.ToTypes();
if (amount < 0)
{
// Changing multiple types is a bit more complicated. Might be a better way (formula?) to do this,
// but essentially just loops between each damage category until all healing is used up.
var healingLeft = -amount;
var healThisCycle = 1;
// While we have healing left...
while (healingLeft > 0 && healThisCycle != 0)
{
// Infinite loop fallback, if no healing was done in a cycle
// then exit
healThisCycle = 0;
int healPerType;
if (healingLeft < types.Count)
{
// Say we were to distribute 2 healing between 3
// this will distribute 1 to each (and stop after 2 are given)
healPerType = 1;
}
else
{
// Say we were to distribute 62 healing between 3
// this will distribute 20 to each, leaving 2 for next loop
healPerType = healingLeft / types.Count;
}
foreach (var type in types)
{
var damage = GetDamage(type);
var healAmount = Math.Min(healingLeft, damage);
healAmount = Math.Min(healAmount, healPerType);
ChangeDamage(type, -healAmount, true);
healThisCycle += healAmount;
healingLeft -= healAmount;
}
}
return true;
}
var damageLeft = amount;
while (damageLeft > 0)
{
int damagePerType;
if (damageLeft < types.Count)
{
damagePerType = 1;
}
else
{
damagePerType = damageLeft / types.Count;
}
foreach (var type in types)
{
var damageAmount = Math.Min(damagePerType, damageLeft);
ChangeDamage(type, damageAmount, true);
damageLeft -= damageAmount;
}
}
return true;
}
public bool SetDamage(DamageType type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
{
if (newValue >= TotalDamage && HasFlag(DamageFlag.Invulnerable))
{
return false;
}
if (newValue < 0)
{
return false;
}
if (!_damageList.ContainsKey(type))
{
return false;
}
var old = _damageList[type];
_damageList[type] = newValue;
var delta = newValue - old;
var datum = new DamageChangeData(type, 0, delta);
var data = new List<DamageChangeData> {datum};
OnHealthChanged(data);
return true;
}
public void ForceHealthChangedEvent()
{
var data = new List<DamageChangeData>();
foreach (var type in _supportedTypes)
{
var damage = GetDamage(type);
var datum = new DamageChangeData(type, damage, 0);
data.Add(datum);
}
OnHealthChanged(data);
}
private void OnHealthChanged(List<DamageChangeData> changes)
{
var args = new DamageChangedEventArgs(this, changes);
OnHealthChanged(args);
}
protected virtual void OnHealthChanged(DamageChangedEventArgs e)
{
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, e);
var message = new DamageChangedMessage(this, e.Data);
SendMessage(message);
Dirty();
}
void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
{
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
ChangeDamage(DamageType.Radiation, totalDamage, false, radiation.Owner);
}
public void OnExplosion(ExplosionEventArgs eventArgs)
{
var damage = eventArgs.Severity switch
{
ExplosionSeverity.Light => 20,
ExplosionSeverity.Heavy => 60,
ExplosionSeverity.Destruction => 250,
_ => throw new ArgumentOutOfRangeException()
};
ChangeDamage(DamageType.Piercing, damage, false);
ChangeDamage(DamageType.Heat, damage, false);
}
}
[Serializable, NetSerializable]
public class DamageableComponentState : ComponentState
{
public readonly Dictionary<DamageType, int> DamageList;
public readonly DamageFlag Flags;
public DamageableComponentState(Dictionary<DamageType, int> damageList, DamageFlag flags) : base(ContentNetIDs.DAMAGEABLE)
{
DamageList = damageList;
Flags = flags;
}
}
}

View File

@@ -1,164 +0,0 @@
#nullable enable
using System.Collections.Generic;
using Content.Shared.Damage;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Damage
{
public interface IDamageableComponent : IComponent, IExAct
{
/// <summary>
/// Sum of all damages taken.
/// </summary>
int TotalDamage { get; }
/// <summary>
/// The amount of damage mapped by <see cref="DamageClass"/>.
/// </summary>
IReadOnlyDictionary<DamageClass, int> DamageClasses { get; }
/// <summary>
/// The amount of damage mapped by <see cref="DamageType"/>.
/// </summary>
IReadOnlyDictionary<DamageType, int> DamageTypes { get; }
/// <summary>
/// The damage flags on this component.
/// </summary>
DamageFlag Flags { get; }
/// <summary>
/// Adds a flag to this component.
/// </summary>
/// <param name="flag">The flag to add.</param>
void AddFlag(DamageFlag flag);
/// <summary>
/// Checks whether or not this component has a specific flag.
/// </summary>
/// <param name="flag">The flag to check for.</param>
/// <returns>True if it has the flag, false otherwise.</returns>
bool HasFlag(DamageFlag flag);
/// <summary>
/// Removes a flag from this component.
/// </summary>
/// <param name="flag">The flag to remove.</param>
void RemoveFlag(DamageFlag flag);
bool SupportsDamageClass(DamageClass @class);
bool SupportsDamageType(DamageType type);
/// <summary>
/// Gets the amount of damage of a type.
/// </summary>
/// <param name="type">The type to get the damage of.</param>
/// <param name="damage">The amount of damage of that type.</param>
/// <returns>
/// True if the given <see cref="type"/> is supported, false otherwise.
/// </returns>
bool TryGetDamage(DamageType type, out int damage);
/// <summary>
/// Gets the amount of damage of a class.
/// </summary>
/// <param name="class">The class to get the damage of.</param>
/// <param name="damage">The amount of damage of that class.</param>
/// <returns>
/// True if the given <see cref="@class"/> is supported, false otherwise.
/// </returns>
bool TryGetDamage(DamageClass @class, out int damage);
/// <summary>
/// Changes the specified <see cref="DamageType"/>, applying
/// resistance values only if it is damage.
/// </summary>
/// <param name="type">Type of damage being changed.</param>
/// <param name="amount">
/// Amount of damage being received (positive for damage, negative for heals).
/// </param>
/// <param name="ignoreResistances">
/// Whether or not to ignore resistances.
/// Healing always ignores resistances, regardless of this input.
/// </param>
/// <param name="source">
/// The entity that dealt or healed the damage, if any.
/// </param>
/// <param name="extraParams">
/// Extra parameters that some components may require, such as a specific limb to target.
/// </param>
/// <returns>
/// False if the given type is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
bool ChangeDamage(
DamageType type,
int amount,
bool ignoreResistances,
IEntity? source = null,
DamageChangeParams? extraParams = null);
/// <summary>
/// Changes the specified <see cref="DamageClass"/>, applying
/// resistance values only if it is damage.
/// Spreads amount evenly between the <see cref="DamageType"></see>s
/// represented by that class.
/// </summary>
/// <param name="class">Class of damage being changed.</param>
/// <param name="amount">
/// Amount of damage being received (positive for damage, negative for heals).
/// </param>
/// <param name="ignoreResistances">
/// Whether to ignore resistances.
/// Healing always ignores resistances, regardless of this input.
/// </param>
/// <param name="source">Entity that dealt or healed the damage, if any.</param>
/// <param name="extraParams">
/// Extra parameters that some components may require,
/// such as a specific limb to target.
/// </param>
/// <returns>
/// Returns false if the given class is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
bool ChangeDamage(
DamageClass @class,
int amount,
bool ignoreResistances,
IEntity? source = null,
DamageChangeParams? extraParams = null);
/// <summary>
/// Forcefully sets the specified <see cref="DamageType"/> to the given
/// value, ignoring resistance values.
/// </summary>
/// <param name="type">Type of damage being changed.</param>
/// <param name="newValue">New damage value to be set.</param>
/// <param name="source">Entity that set the new damage value.</param>
/// <param name="extraParams">
/// Extra parameters that some components may require,
/// such as a specific limb to target.
/// </param>
/// <returns>
/// Returns false if the given type is not supported or improper
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
/// </returns>
bool SetDamage(
DamageType type,
int newValue,
IEntity? source = null,
DamageChangeParams? extraParams = null);
/// <summary>
/// Sets all damage values to zero.
/// </summary>
void Heal();
/// <summary>
/// Invokes the HealthChangedEvent with the current values of health.
/// </summary>
void ForceHealthChangedEvent();
}
}

View File

@@ -1,109 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.Components.Disposal
{
public abstract class SharedDisposalMailingUnitComponent : SharedDisposalUnitComponent
{
public override string Name => "DisposalMailingUnit";
public const string TAGS_MAIL = "mail";
public const string NET_TAG = "tag";
public const string NET_SRC = "src";
public const string NET_TARGET = "target";
public const string NET_CMD_SENT = "mail_sent";
public const string NET_CMD_REQUEST = "get_mailer_tag";
public const string NET_CMD_RESPONSE = "mailer_tag";
[Serializable, NetSerializable]
public new enum UiButton : byte
{
Eject,
Engage,
Power
}
[Serializable, NetSerializable]
public class DisposalMailingUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable<DisposalMailingUnitBoundUserInterfaceState>, ICloneable
{
public readonly string UnitName;
public readonly string UnitState;
public readonly float Pressure;
public readonly bool Powered;
public readonly bool Engaged;
public readonly string Tag;
public readonly List<string> Tags;
public readonly string? Target;
public DisposalMailingUnitBoundUserInterfaceState(string unitName, string unitState, float pressure, bool powered,
bool engaged, string tag, List<string> tags, string? target)
{
UnitName = unitName;
UnitState = unitState;
Pressure = pressure;
Powered = powered;
Engaged = engaged;
Tag = tag;
Tags = tags;
Target = target;
}
public object Clone()
{
return new DisposalMailingUnitBoundUserInterfaceState(UnitName, UnitState, Pressure, Powered, Engaged, Tag, (List<string>)Tags.Clone(), Target);
}
public bool Equals(DisposalMailingUnitBoundUserInterfaceState? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
return UnitName == other.UnitName &&
UnitState == other.UnitState &&
Powered == other.Powered &&
Engaged == other.Engaged &&
Pressure.Equals(other.Pressure) &&
Tag == other.Tag &&
Target == other.Target;
}
}
/// <summary>
/// Message data sent from client to server when a mailing unit ui button is pressed.
/// </summary>
[Serializable, NetSerializable]
public new class UiButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public UiButtonPressedMessage(UiButton button)
{
Button = button;
}
}
/// <summary>
/// Message data sent from client to server when the mailing units target is updated.
/// </summary>
[Serializable, NetSerializable]
public class UiTargetUpdateMessage : BoundUserInterfaceMessage
{
public readonly string? Target;
public UiTargetUpdateMessage(string? target)
{
Target = target;
}
}
[Serializable, NetSerializable]
public enum DisposalMailingUnitUiKey
{
Key
}
}
}

View File

@@ -1,55 +0,0 @@
#nullable enable
using System;
using System.Text.RegularExpressions;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Disposal
{
public class SharedDisposalRouterComponent : Component
{
public override string Name => "DisposalRouter";
public static readonly Regex TagRegex = new("^[a-zA-Z0-9, ]*$", RegexOptions.Compiled);
[Serializable, NetSerializable]
public class DisposalRouterUserInterfaceState : BoundUserInterfaceState
{
public readonly string Tags;
public DisposalRouterUserInterfaceState(string tags)
{
Tags = tags;
}
}
[Serializable, NetSerializable]
public class UiActionMessage : BoundUserInterfaceMessage
{
public readonly UiAction Action;
public readonly string Tags = "";
public UiActionMessage(UiAction action, string tags)
{
Action = action;
if (Action == UiAction.Ok)
{
Tags = tags.Substring(0, Math.Min(tags.Length, 150));
}
}
}
[Serializable, NetSerializable]
public enum UiAction
{
Ok
}
[Serializable, NetSerializable]
public enum DisposalRouterUiKey
{
Key
}
}
}

View File

@@ -1,55 +0,0 @@
#nullable enable
using System;
using System.Text.RegularExpressions;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Disposal
{
public class SharedDisposalTaggerComponent : Component
{
public override string Name => "DisposalTagger";
public static readonly Regex TagRegex = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled);
[Serializable, NetSerializable]
public class DisposalTaggerUserInterfaceState : BoundUserInterfaceState
{
public readonly string Tag;
public DisposalTaggerUserInterfaceState(string tag)
{
Tag = tag;
}
}
[Serializable, NetSerializable]
public class UiActionMessage : BoundUserInterfaceMessage
{
public readonly UiAction Action;
public readonly string Tag = "";
public UiActionMessage(UiAction action, string tag)
{
Action = action;
if (Action == UiAction.Ok)
{
Tag = tag.Substring(0, Math.Min(tag.Length, 30));
}
}
}
[Serializable, NetSerializable]
public enum UiAction
{
Ok
}
[Serializable, NetSerializable]
public enum DisposalTaggerUiKey
{
Key
}
}
}

View File

@@ -1,20 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Disposal
{
[Serializable, NetSerializable]
public enum DisposalTubeVisuals
{
VisualState
}
[Serializable, NetSerializable]
public enum DisposalTubeVisualState
{
Free = 0,
Anchored,
Broken,
}
}

View File

@@ -1,157 +0,0 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Disposal
{
public abstract class SharedDisposalUnitComponent : Component, IDragDropOn
{
public override string Name => "DisposalUnit";
[ViewVariables]
public bool Anchored =>
!Owner.TryGetComponent(out IPhysBody? physics) ||
physics.BodyType == BodyType.Static;
[Serializable, NetSerializable]
public enum Visuals
{
VisualState,
Handle,
Light
}
[Serializable, NetSerializable]
public enum VisualState
{
UnAnchored,
Anchored,
Flushing,
Charging
}
[Serializable, NetSerializable]
public enum HandleState
{
Normal,
Engaged
}
[Serializable, NetSerializable]
public enum LightState
{
Off,
Charging,
Full,
Ready
}
[Serializable, NetSerializable]
public enum UiButton
{
Eject,
Engage,
Power
}
[Serializable, NetSerializable]
public enum PressureState
{
Ready,
Pressurizing
}
public virtual void Update(float frameTime)
{
return;
}
[Serializable, NetSerializable]
public class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable<DisposalUnitBoundUserInterfaceState>
{
public readonly string UnitName;
public readonly string UnitState;
public readonly float Pressure;
public readonly bool Powered;
public readonly bool Engaged;
public DisposalUnitBoundUserInterfaceState(string unitName, string unitState, float pressure, bool powered,
bool engaged)
{
UnitName = unitName;
UnitState = unitState;
Pressure = pressure;
Powered = powered;
Engaged = engaged;
}
public bool Equals(DisposalUnitBoundUserInterfaceState? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return UnitName == other.UnitName &&
UnitState == other.UnitState &&
Powered == other.Powered &&
Engaged == other.Engaged &&
Pressure.Equals(other.Pressure);
}
}
/// <summary>
/// Message data sent from client to server when a disposal unit ui button is pressed.
/// </summary>
[Serializable, NetSerializable]
public class UiButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public UiButtonPressedMessage(UiButton button)
{
Button = button;
}
}
[Serializable, NetSerializable]
public enum DisposalUnitUiKey
{
Key
}
public virtual bool CanInsert(IEntity entity)
{
if (!Anchored)
return false;
// TODO: Probably just need a disposable tag.
if (!entity.TryGetComponent(out SharedItemComponent? storable) &&
!entity.HasComponent<IBody>())
{
return false;
}
if (!entity.TryGetComponent(out IPhysBody? physics) ||
!physics.CanCollide && storable == null)
{
if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) {
return false;
}
}
return true;
}
public virtual bool CanDragDropOn(DragDropEvent eventArgs)
{
return CanInsert(eventArgs.Dragged);
}
public abstract bool DragDropOn(DragDropEvent eventArgs);
}
}

View File

@@ -1,17 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Doors
{
[Serializable, NetSerializable]
public enum AirlockWireStatus
{
PowerIndicator,
BoltIndicator,
BoltLightIndicator,
AIControlIndicator,
TimingIndicator,
SafetyIndicator,
}
}

View File

@@ -1,177 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Doors
{
public abstract class SharedDoorComponent : Component
{
public override string Name => "Door";
public override uint? NetID => ContentNetIDs.DOOR;
[ComponentDependency]
protected readonly SharedAppearanceComponent? AppearanceComponent = null;
[ComponentDependency]
protected readonly IPhysBody? PhysicsComponent = null;
[ViewVariables]
private DoorState _state = DoorState.Closed;
/// <summary>
/// The current state of the door -- whether it is open, closed, opening, or closing.
/// </summary>
public virtual DoorState State
{
get => _state;
protected set
{
if (_state == value)
{
return;
}
_state = value;
SetAppearance(State switch
{
DoorState.Open => DoorVisualState.Open,
DoorState.Closed => DoorVisualState.Closed,
DoorState.Opening => DoorVisualState.Opening,
DoorState.Closing => DoorVisualState.Closing,
_ => throw new ArgumentOutOfRangeException(),
});
}
}
/// <summary>
/// Closing time until impassable.
/// </summary>
[DataField("closeTimeOne")]
protected TimeSpan CloseTimeOne = TimeSpan.FromSeconds(0.4f);
/// <summary>
/// Closing time until fully closed.
/// </summary>
[DataField("closeTimeTwo")]
protected TimeSpan CloseTimeTwo = TimeSpan.FromSeconds(0.2f);
/// <summary>
/// Opening time until passable.
/// </summary>
[DataField("openTimeOne")]
protected TimeSpan OpenTimeOne = TimeSpan.FromSeconds(0.4f);
/// <summary>
/// Opening time until fully open.
/// </summary>
[DataField("openTimeTwo")]
protected TimeSpan OpenTimeTwo = TimeSpan.FromSeconds(0.2f);
/// <summary>
/// Time to finish denying.
/// </summary>
protected static TimeSpan DenyTime => TimeSpan.FromSeconds(0.45f);
/// <summary>
/// Used by ServerDoorComponent to get the CurTime for the client to use to know when to open, and by ClientDoorComponent to know the CurTime to correctly open.
/// </summary>
[Dependency] protected IGameTiming GameTiming = default!;
/// <summary>
/// The time the door began to open or close, if the door is opening or closing, or null if it is neither.
/// </summary>
protected TimeSpan? StateChangeStartTime = null;
/// <summary>
/// List of EntityUids of entities we're currently crushing. Cleared in OnPartialOpen().
/// </summary>
protected List<EntityUid> CurrentlyCrushing = new();
public bool IsCrushing(IEntity entity)
{
return CurrentlyCrushing.Contains(entity.Uid);
}
protected void SetAppearance(DoorVisualState state)
{
AppearanceComponent?.SetData(DoorVisuals.VisualState, state);
}
/// <summary>
/// Called when the door is partially opened.
/// </summary>
protected virtual void OnPartialOpen()
{
if (PhysicsComponent != null)
{
PhysicsComponent.CanCollide = false;
}
// we can't be crushing anyone anymore, since we're opening
CurrentlyCrushing.Clear();
}
/// <summary>
/// Called when the door is partially closed.
/// </summary>
protected virtual void OnPartialClose()
{
if (PhysicsComponent != null)
{
PhysicsComponent.CanCollide = true;
}
}
[Serializable, NetSerializable]
public enum DoorState
{
Open,
Closed,
Opening,
Closing,
}
}
[Serializable, NetSerializable]
public enum DoorVisualState
{
Open,
Closed,
Opening,
Closing,
Deny,
Welded
}
[Serializable, NetSerializable]
public enum DoorVisuals
{
VisualState,
Powered,
BoltLights
}
[Serializable, NetSerializable]
public class DoorComponentState : ComponentState
{
public readonly SharedDoorComponent.DoorState DoorState;
public readonly TimeSpan? StartTime;
public readonly List<EntityUid> CurrentlyCrushing;
public readonly TimeSpan CurTime;
public DoorComponentState(SharedDoorComponent.DoorState doorState, TimeSpan? startTime, List<EntityUid> currentlyCrushing, TimeSpan curTime) : base(ContentNetIDs.DOOR)
{
DoorState = doorState;
StartTime = startTime;
CurrentlyCrushing = currentlyCrushing;
CurTime = curTime;
}
}
}

View File

@@ -1,12 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Explosion
{
[Serializable, NetSerializable]
public enum ClusterFlashVisuals : byte
{
GrenadesCounter
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components
{
[Serializable, NetSerializable]
public enum ItemCabinetVisuals : byte
{
IsOpen,
ContainsItem
}
}

View File

@@ -1,18 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Fluids
{
public class SharedSprayComponent : Component
{
public override string Name => "Spray";
}
[Serializable, NetSerializable]
public enum SprayVisuals
{
Safety,
}
}

View File

@@ -1,87 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
namespace Content.Shared.GameObjects.Components.GUI
{
public abstract class SharedStrippableComponent : Component, IDraggable
{
public override string Name => "Strippable";
public bool CanBeStripped(IEntity by)
{
return by != Owner
&& by.HasComponent<ISharedHandsComponent>()
&& ActionBlockerSystem.CanInteract(by);
}
bool IDraggable.CanDrop(CanDropEvent args)
{
return args.Target != args.Dragged
&& args.Target == args.User
&& CanBeStripped(args.User);
}
public abstract bool Drop(DragDropEvent args);
[NetSerializable, Serializable]
public enum StrippingUiKey
{
Key,
}
}
[NetSerializable, Serializable]
public class StrippingInventoryButtonPressed : BoundUserInterfaceMessage
{
public Slots Slot { get; }
public StrippingInventoryButtonPressed(Slots slot)
{
Slot = slot;
}
}
[NetSerializable, Serializable]
public class StrippingHandButtonPressed : BoundUserInterfaceMessage
{
public string Hand { get; }
public StrippingHandButtonPressed(string hand)
{
Hand = hand;
}
}
[NetSerializable, Serializable]
public class StrippingHandcuffButtonPressed : BoundUserInterfaceMessage
{
public EntityUid Handcuff { get; }
public StrippingHandcuffButtonPressed(EntityUid handcuff)
{
Handcuff = handcuff;
}
}
[NetSerializable, Serializable]
public class StrippingBoundUserInterfaceState : BoundUserInterfaceState
{
public Dictionary<Slots, string> Inventory { get; }
public Dictionary<string, string> Hands { get; }
public Dictionary<EntityUid, string> Handcuffs { get; }
public StrippingBoundUserInterfaceState(Dictionary<Slots, string> inventory, Dictionary<string, string> hands, Dictionary<EntityUid, string> handcuffs)
{
Inventory = inventory;
Hands = hands;
Handcuffs = handcuffs;
}
}
}

View File

@@ -1,27 +0,0 @@
#nullable enable
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.GUI
{
/// <summary>
/// Give to an entity to say they can strip another entity.
/// </summary>
[RegisterComponent]
public class SharedStrippingComponent : Component, IDragDropOn
{
public override string Name => "Stripping";
bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
{
if (!eventArgs.Dragged.TryGetComponent(out SharedStrippableComponent? strippable)) return false;
return strippable.CanBeStripped(Owner);
}
bool IDragDropOn.DragDropOn(DragDropEvent eventArgs)
{
// Handled by StrippableComponent
return true;
}
}
}

View File

@@ -1,73 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Gravity
{
public class SharedGravityGeneratorComponent : Component
{
public override string Name => "GravityGenerator";
public override uint? NetID => ContentNetIDs.GRAVITY_GENERATOR;
/// <summary>
/// Sent to the server to set whether the generator should be on or off
/// </summary>
[Serializable, NetSerializable]
public class SwitchGeneratorMessage : BoundUserInterfaceMessage
{
public bool On;
public SwitchGeneratorMessage(bool on)
{
On = on;
}
}
/// <summary>
/// Sent to the server when requesting the status of the generator
/// </summary>
[Serializable, NetSerializable]
public class GeneratorStatusRequestMessage : BoundUserInterfaceMessage
{
public GeneratorStatusRequestMessage()
{
}
}
[Serializable, NetSerializable]
public class GeneratorState : BoundUserInterfaceState
{
public bool On;
public GeneratorState(bool on)
{
On = on;
}
}
[Serializable, NetSerializable]
public enum GravityGeneratorUiKey
{
Key
}
}
[Serializable, NetSerializable]
public enum GravityGeneratorVisuals
{
State,
CoreVisible
}
[Serializable, NetSerializable]
public enum GravityGeneratorStatus
{
Broken,
Unpowered,
Off,
On
}
}

View File

@@ -1,93 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Audio.Midi;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Instruments
{
public class SharedInstrumentComponent : Component
{
public override string Name => "Instrument";
public override uint? NetID => ContentNetIDs.INSTRUMENTS;
[ViewVariables(VVAccess.ReadWrite)]
public virtual byte InstrumentProgram { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public virtual byte InstrumentBank { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool AllowPercussion { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool AllowProgramChange { get ; set; }
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool RespectMidiLimits { get; set; }
public virtual void Update(float delta)
{
}
}
/// <summary>
/// This message is sent to the client to completely stop midi input and midi playback.
/// </summary>
[Serializable, NetSerializable]
public class InstrumentStopMidiMessage : ComponentMessage
{
}
/// <summary>
/// This message is sent to the client to start the synth.
/// </summary>
[Serializable, NetSerializable]
public class InstrumentStartMidiMessage : ComponentMessage
{
}
/// <summary>
/// This message carries a MidiEvent to be played on clients.
/// </summary>
[Serializable, NetSerializable]
public class InstrumentMidiEventMessage : ComponentMessage
{
public MidiEvent[] MidiEvent;
public InstrumentMidiEventMessage(MidiEvent[] midiEvent)
{
MidiEvent = midiEvent;
}
}
[Serializable, NetSerializable]
public class InstrumentState : ComponentState
{
public bool Playing { get; }
public byte InstrumentProgram { get; }
public byte InstrumentBank { get; }
public bool AllowPercussion { get; }
public bool AllowProgramChange { get; }
public bool RespectMidiLimits { get; }
public InstrumentState(bool playing, byte instrumentProgram, byte instrumentBank, bool allowPercussion, bool allowProgramChange, bool respectMidiLimits, uint sequencerTick = 0) : base(ContentNetIDs.INSTRUMENTS)
{
Playing = playing;
InstrumentProgram = instrumentProgram;
InstrumentBank = instrumentBank;
AllowPercussion = allowPercussion;
AllowProgramChange = allowProgramChange;
RespectMidiLimits = respectMidiLimits;
}
}
[NetSerializable, Serializable]
public enum InstrumentUiKey
{
Key,
}
}

View File

@@ -1,71 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Interactable
{
[Flags]
public enum ToolQuality : byte
{
None = 0,
Anchoring = 1,
Prying = 1 << 1,
Screwing = 1 << 2,
Cutting = 1 << 3,
Welding = 1 << 4,
Multitool = 1 << 5,
}
public static class ToolQualityHelpers
{
public static string GetToolName(this ToolQuality quality)
{
return quality switch
{
ToolQuality.Anchoring => "Wrench",
ToolQuality.Prying => "Crowbar",
ToolQuality.Screwing => "Screwdriver",
ToolQuality.Cutting => "Wirecutters",
ToolQuality.Welding => "Welding tool",
ToolQuality.Multitool => "Multitool",
_ => throw new ArgumentOutOfRangeException()
};
}
}
public class SharedToolComponent : Component
{
public override string Name => "Tool";
public virtual ToolQuality Qualities { get; set; }
}
[NetSerializable, Serializable]
public class MultiToolComponentState : ComponentState
{
public ToolQuality Quality { get; }
public MultiToolComponentState(ToolQuality quality) : base(ContentNetIDs.MULTITOOLS)
{
Quality = quality;
}
}
[NetSerializable, Serializable]
public class WelderComponentState : ComponentState
{
public float FuelCapacity { get; }
public float Fuel { get; }
public bool Activated { get; }
public ToolQuality Quality { get; }
public WelderComponentState(float fuelCapacity, float fuel, bool activated) : base(ContentNetIDs.WELDER)
{
FuelCapacity = fuelCapacity;
Fuel = fuel;
Activated = activated;
Quality = ToolQuality.Welding;
}
}
}

View File

@@ -1,153 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Inventory
{
public static class EquipmentSlotDefines
{
public static IReadOnlyCollection<Slots> AllSlots { get; }
static EquipmentSlotDefines()
{
var output = new Slots[(int)Slots.LAST - (int)Slots.HEAD];
// The index stuff is to jump over NONE.
for (var i = 0; i < output.Length; i++)
{
output[i] = (Slots)(i+1);
}
AllSlots = output;
}
/// <summary>
/// Uniquely identifies a single slot in an inventory.
/// </summary>
[Serializable, NetSerializable]
public enum Slots : byte
{
NONE = 0,
HEAD,
EYES,
EARS,
MASK,
OUTERCLOTHING,
INNERCLOTHING,
NECK,
BACKPACK,
BELT,
GLOVES,
SHOES,
IDCARD,
POCKET1,
POCKET2,
/// <summary>
/// Not a real slot.
/// </summary>
LAST
}
/// <summary>
/// Defines what slot types an item can fit into.
/// </summary>
[Serializable, NetSerializable]
[Flags]
public enum SlotFlags
{
NONE = 0,
PREVENTEQUIP = 1 << 0,
HEAD = 1 << 1,
HELMET = 1 << 1,
EYES = 1 << 2,
EARS = 1 << 3,
MASK = 1 << 4,
OUTERCLOTHING = 1 << 5,
INNERCLOTHING = 1 << 6,
NECK = 1 << 7,
BACK = 1 << 8,
BACKPACK = 1 << 8,
BELT = 1 << 9,
GLOVES = 1 << 10,
HAND = 1 << 10,
IDCARD = 1 << 11,
POCKET = 1 << 12,
LEGS = 1 << 13,
SHOES = 1 << 14,
FEET = 1 << 14,
}
public static readonly IReadOnlyDictionary<Slots, string> SlotNames = new Dictionary<Slots, string>()
{
{Slots.HEAD, "Head"},
{Slots.EYES, "Eyes"},
{Slots.EARS, "Ears"},
{Slots.MASK, "Mask"},
{Slots.OUTERCLOTHING, "Outer Clothing"},
{Slots.INNERCLOTHING, "Inner Clothing"},
{Slots.NECK, "Neck"},
{Slots.BACKPACK, "Backpack"},
{Slots.BELT, "Belt"},
{Slots.GLOVES, "Gloves"},
{Slots.SHOES, "Shoes"},
{Slots.IDCARD, "Id Card"},
{Slots.POCKET1, "Left Pocket"},
{Slots.POCKET2, "Right Pocket"},
};
/// <summary>
/// Defines which slot types fit in which slots.
/// </summary>
/// <remarks>
/// Note that this is not exhaustive. Inventory implementations can provide additional behavior.
/// </remarks>
public static readonly IReadOnlyDictionary<Slots, SlotFlags> SlotMasks = new Dictionary<Slots, SlotFlags>()
{
{Slots.HEAD, SlotFlags.HEAD},
{Slots.EYES, SlotFlags.EYES},
{Slots.EARS, SlotFlags.EARS},
{Slots.MASK, SlotFlags.MASK},
{Slots.OUTERCLOTHING, SlotFlags.OUTERCLOTHING},
{Slots.INNERCLOTHING, SlotFlags.INNERCLOTHING},
{Slots.NECK, SlotFlags.NECK},
{Slots.BACKPACK, SlotFlags.BACK},
{Slots.BELT, SlotFlags.BELT},
{Slots.GLOVES, SlotFlags.GLOVES},
{Slots.SHOES, SlotFlags.FEET},
{Slots.IDCARD, SlotFlags.IDCARD},
{Slots.POCKET1, SlotFlags.POCKET},
{Slots.POCKET2, SlotFlags.POCKET},
};
// for shared string dict, since we don't define these anywhere in content
[UsedImplicitly]
public static readonly string[] _inventorySlotStrings =
{
"Inventory_HEAD",
"Inventory_EYES",
"Inventory_EARS",
"Inventory_MASK",
"Inventory_OUTERCLOTHING",
"Inventory_INNERCLOTHING",
"Inventory_NECK",
"Inventory_BACKPACK",
"Inventory_BELT",
"Inventory_GLOVES",
"Inventory_SHOES",
"Inventory_IDCARD",
"Inventory_POCKET1",
"Inventory_POCKET2",
};
// for shared string dict, since we don't define these anywhere in content
[UsedImplicitly]
public static readonly string[] _handsSlotStrings =
{
"Hands_left",
"Hands_right",
};
}
}

View File

@@ -1,62 +0,0 @@
#nullable enable
using System.Collections.Generic;
using JetBrains.Annotations;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
namespace Content.Shared.GameObjects.Components.Inventory
{
public abstract class Inventory
{
public abstract string InterfaceControllerTypeName { get; }
public abstract IReadOnlyList<Slots> SlotMasks { get; }
/// <summary>
/// Gets the drawing order of a slot.
/// </summary>
/// <returns>
/// An int that can be used for sorting relative to other drawing orders.
/// The value returned does not mean anything else.
/// </returns>
public abstract int SlotDrawingOrder(Slots slot);
}
// Dynamically created by SharedInventoryComponent.
[UsedImplicitly]
public class HumanInventory : Inventory
{
public override string InterfaceControllerTypeName => "HumanInventoryInterfaceController";
private static readonly Dictionary<Slots, int> _slotDrawingOrder = new()
{
{Slots.POCKET1, 13},
{Slots.POCKET2, 12},
{Slots.HEAD, 11},
{Slots.MASK, 10},
{Slots.EARS, 9},
{Slots.NECK, 8},
{Slots.BACKPACK, 7},
{Slots.EYES, 6},
{Slots.OUTERCLOTHING, 5},
{Slots.BELT, 4},
{Slots.GLOVES, 3},
{Slots.SHOES, 2},
{Slots.IDCARD, 1},
{Slots.INNERCLOTHING, 0}
};
public override IReadOnlyList<Slots> SlotMasks { get; } = new List<Slots>()
{
Slots.EYES, Slots.HEAD, Slots.EARS,
Slots.OUTERCLOTHING, Slots.MASK, Slots.INNERCLOTHING,
Slots.BACKPACK, Slots.BELT, Slots.GLOVES,
Slots.NONE, Slots.SHOES, Slots.IDCARD, Slots.POCKET1, Slots.POCKET2,
Slots.NECK
};
public override int SlotDrawingOrder(Slots slot)
{
return _slotDrawingOrder.TryGetValue(slot, out var val) ? val : 0;
}
}
}

View File

@@ -1,100 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
namespace Content.Shared.GameObjects.Components.Inventory
{
public abstract class SharedInventoryComponent : Component, IMoveSpeedModifier
{
[Dependency] protected readonly IReflectionManager ReflectionManager = default!;
[Dependency] protected readonly IDynamicTypeFactory DynamicTypeFactory = default!;
public sealed override string Name => "Inventory";
public sealed override uint? NetID => ContentNetIDs.STORAGE;
[ViewVariables]
protected Inventory InventoryInstance { get; private set; } = default!;
[ViewVariables]
[DataField("Template")]
private string _templateName = "HumanInventory"; //stored for serialization purposes
public override void Initialize()
{
base.Initialize();
CreateInventory();
}
private void CreateInventory()
{
var type = ReflectionManager.LooseGetType(_templateName);
DebugTools.Assert(type != null);
InventoryInstance = DynamicTypeFactory.CreateInstance<Inventory>(type!);
}
/// <returns>true if the item is equipped to an equip slot (NOT inside an equipped container
/// like inside a backpack)</returns>
public abstract bool IsEquipped(IEntity item);
[Serializable, NetSerializable]
protected class InventoryComponentState : ComponentState
{
public List<KeyValuePair<Slots, EntityUid>> Entities { get; }
public KeyValuePair<Slots, (EntityUid entity, bool fits)>? HoverEntity { get; }
public InventoryComponentState(List<KeyValuePair<Slots, EntityUid>> entities, KeyValuePair<Slots, (EntityUid entity, bool fits)>? hoverEntity = null) : base(ContentNetIDs.STORAGE)
{
Entities = entities;
HoverEntity = hoverEntity;
}
}
[Serializable, NetSerializable]
public class ClientInventoryMessage : ComponentMessage
{
public Slots Inventoryslot;
public ClientInventoryUpdate Updatetype;
public ClientInventoryMessage(Slots inventoryslot, ClientInventoryUpdate updatetype)
{
Directed = true;
Inventoryslot = inventoryslot;
Updatetype = updatetype;
}
public enum ClientInventoryUpdate
{
Equip = 0,
Use = 1,
Hover = 2
}
}
/// <summary>
/// Component message for opening the Storage UI of item in Slot
/// </summary>
[Serializable, NetSerializable]
public class OpenSlotStorageUIMessage : ComponentMessage
{
public Slots Slot;
public OpenSlotStorageUIMessage(Slots slot)
{
Directed = true;
Slot = slot;
}
}
public abstract float WalkSpeedModifier { get; }
public abstract float SprintSpeedModifier { get; }
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Items
{
[Serializable, NetSerializable]
public class ClothingComponentState : ComponentState
{
public string? ClothingEquippedPrefix { get; }
public string? EquippedPrefix { get; }
public ClothingComponentState(string? clothingEquippedPrefix, string? equippedPrefix) : base(ContentNetIDs.CLOTHING)
{
ClothingEquippedPrefix = clothingEquippedPrefix;
EquippedPrefix = equippedPrefix;
}
}
}

View File

@@ -1,9 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Items
{
public interface ISharedHandsComponent : IComponent
{
}
}

View File

@@ -1,87 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Items
{
/// <summary>
/// Stores a visual "cooldown" for items, that gets displayed in the hands GUI.
/// </summary>
[RegisterComponent]
public sealed class ItemCooldownComponent : Component
{
public override string Name => "ItemCooldown";
public override uint? NetID => ContentNetIDs.ITEMCOOLDOWN;
private TimeSpan? _cooldownEnd;
private TimeSpan? _cooldownStart;
/// <summary>
/// The time when this cooldown ends.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables]
public TimeSpan? CooldownEnd
{
get => _cooldownEnd;
set
{
_cooldownEnd = value;
Dirty();
}
}
/// <summary>
/// The time when this cooldown started.
/// </summary>
/// <remarks>
/// If null, no cooldown is displayed.
/// </remarks>
[ViewVariables]
public TimeSpan? CooldownStart
{
get => _cooldownStart;
set
{
_cooldownStart = value;
Dirty();
}
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new ItemCooldownComponentState
{
CooldownEnd = CooldownEnd,
CooldownStart = CooldownStart
};
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not ItemCooldownComponentState cast)
return;
CooldownStart = cast.CooldownStart;
CooldownEnd = cast.CooldownEnd;
}
[Serializable, NetSerializable]
private sealed class ItemCooldownComponentState : ComponentState
{
public TimeSpan? CooldownStart { get; set; }
public TimeSpan? CooldownEnd { get; set; }
public ItemCooldownComponentState() : base(ContentNetIDs.ITEMCOOLDOWN)
{
}
}
}
}

View File

@@ -1,146 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Items
{
public abstract class SharedHandsComponent : Component, ISharedHandsComponent
{
public sealed override string Name => "Hands";
public sealed override uint? NetID => ContentNetIDs.HANDS;
/// <returns>true if the item is in one of the hands</returns>
public abstract bool IsHolding(IEntity item);
}
[Serializable, NetSerializable]
public sealed class SharedHand
{
public readonly int Index;
public readonly string Name;
public readonly EntityUid? EntityUid;
public readonly HandLocation Location;
public readonly bool Enabled;
public SharedHand(int index, string name, EntityUid? entityUid, HandLocation location, bool enabled)
{
Index = index;
Name = name;
EntityUid = entityUid;
Location = location;
Enabled = enabled;
}
}
// The IDs of the items get synced over the network.
[Serializable, NetSerializable]
public class HandsComponentState : ComponentState
{
public readonly SharedHand[] Hands;
public readonly string? ActiveIndex;
public HandsComponentState(SharedHand[] hands, string? activeIndex) : base(ContentNetIDs.HANDS)
{
Hands = hands;
ActiveIndex = activeIndex;
}
}
/// <summary>
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
/// </summary>
[Serializable, NetSerializable]
public class UseInHandMsg : ComponentMessage
{
public UseInHandMsg()
{
Directed = true;
}
}
/// <summary>
/// A message that calls the activate interaction on the item in Index.
/// </summary>
[Serializable, NetSerializable]
public class ActivateInHandMsg : ComponentMessage
{
public string Index { get; }
public ActivateInHandMsg(string index)
{
Directed = true;
Index = index;
}
}
[Serializable, NetSerializable]
public class ClientAttackByInHandMsg : ComponentMessage
{
public string Index { get; }
public ClientAttackByInHandMsg(string index)
{
Directed = true;
Index = index;
}
}
[Serializable, NetSerializable]
public class ClientChangedHandMsg : ComponentMessage
{
public string Index { get; }
public ClientChangedHandMsg(string index)
{
Directed = true;
Index = index;
}
}
[Serializable, NetSerializable]
public class HandEnabledMsg : ComponentMessage
{
public string Name { get; }
public HandEnabledMsg(string name)
{
Name = name;
}
}
[Serializable, NetSerializable]
public class HandDisabledMsg : ComponentMessage
{
public string Name { get; }
public HandDisabledMsg(string name)
{
Name = name;
}
}
public enum HandLocation : byte
{
Left,
Middle,
Right
}
/// <summary>
/// Component message for displaying an animation of an entity flying towards the owner of a HandsComponent
/// </summary>
[Serializable, NetSerializable]
public class AnimatePickupEntityMessage : ComponentMessage
{
public readonly EntityUid EntityId;
public readonly EntityCoordinates EntityPosition;
public AnimatePickupEntityMessage(EntityUid entity, EntityCoordinates entityPosition)
{
Directed = true;
EntityId = entity;
EntityPosition = entityPosition;
}
}
}

View File

@@ -1,13 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Items
{
[RegisterComponent]
public class ThrownItemComponent : Component
{
public override string Name => "ThrownItem";
public IEntity? Thrower { get; set; }
}
}

View File

@@ -1,11 +0,0 @@
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.MachineLinking
{
[Serializable, NetSerializable]
public enum SignalSwitchVisuals
{
On
}
}

View File

@@ -1,20 +0,0 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.MachineLinking
{
[Serializable, NetSerializable]
public enum TwoWayLeverVisuals : byte
{
State
}
[Serializable, NetSerializable]
public enum TwoWayLeverSignal : byte
{
Middle,
Left,
Right
}
}

View File

@@ -1,10 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.Components.Markers
{
public class SharedSpawnPointComponent : Component
{
public sealed override string Name => "SpawnPoint";
}
}

View File

@@ -1,83 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Medical
{
public class SharedCloningPodComponent : Component
{
public override string Name => "CloningPod";
[Serializable, NetSerializable]
public class CloningPodBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly Dictionary<int, string?> MindIdName;
// When this state was created.
// The reason this is used rather than a start time is because cloning can be interrupted.
public readonly TimeSpan ReferenceTime;
// Both of these are in seconds.
// They're not TimeSpans because of complicated reasons.
// CurTime of receipt is combined with Progress.
public readonly float Progress;
public readonly float Maximum;
// If true, cloning is progressing (predict clone progress)
public readonly bool Progressing;
public readonly bool MindPresent;
public CloningPodBoundUserInterfaceState(Dictionary<int, string?> mindIdName, TimeSpan refTime, float progress, float maximum, bool progressing, bool mindPresent)
{
MindIdName = mindIdName;
ReferenceTime = refTime;
Progress = progress;
Maximum = maximum;
Progressing = progressing;
MindPresent = mindPresent;
}
}
[Serializable, NetSerializable]
public enum CloningPodUIKey
{
Key
}
[Serializable, NetSerializable]
public enum CloningPodVisuals
{
Status
}
[Serializable, NetSerializable]
public enum CloningPodStatus
{
Idle,
Cloning,
Gore,
NoMind
}
[Serializable, NetSerializable]
public enum UiButton
{
Clone,
Eject
}
[Serializable, NetSerializable]
public class CloningPodUiButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public readonly int? ScanId;
public CloningPodUiButtonPressedMessage(UiButton button, int? scanId)
{
Button = button;
ScanId = scanId;
}
}
}
}

View File

@@ -1,90 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Medical
{
public abstract class SharedMedicalScannerComponent : Component, IDragDropOn
{
public override string Name => "MedicalScanner";
[Serializable, NetSerializable]
public class MedicalScannerBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly EntityUid? Entity;
public readonly Dictionary<DamageClass, int> DamageClasses;
public readonly Dictionary<DamageType, int> DamageTypes;
public readonly bool IsScanned;
public MedicalScannerBoundUserInterfaceState(
EntityUid? entity,
Dictionary<DamageClass, int> damageClasses,
Dictionary<DamageType, int> damageTypes,
bool isScanned)
{
Entity = entity;
DamageClasses = damageClasses;
DamageTypes = damageTypes;
IsScanned = isScanned;
}
public bool HasDamage()
{
return DamageClasses.Count > 0 || DamageTypes.Count > 0;
}
}
[Serializable, NetSerializable]
public enum MedicalScannerUiKey
{
Key
}
[Serializable, NetSerializable]
public enum MedicalScannerVisuals
{
Status
}
[Serializable, NetSerializable]
public enum MedicalScannerStatus
{
Off,
Open,
Red,
Death,
Green,
Yellow,
}
[Serializable, NetSerializable]
public enum UiButton
{
ScanDNA,
}
[Serializable, NetSerializable]
public class UiButtonPressedMessage : BoundUserInterfaceMessage
{
public readonly UiButton Button;
public UiButtonPressedMessage(UiButton button)
{
Button = button;
}
}
bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
{
return eventArgs.Dragged.HasComponent<IBody>();
}
public abstract bool DragDropOn(DragDropEvent eventArgs);
}
}

View File

@@ -1,11 +0,0 @@
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Mining
{
[Serializable, NetSerializable]
public enum AsteroidRockVisuals
{
State
}
}

View File

@@ -1,27 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
namespace Content.Shared.GameObjects.Components.Mobs
{
public static class DamageStateHelpers
{
/// <summary>
/// Enumerates over <see cref="DamageState"/>, returning them in order
/// of alive to dead.
/// </summary>
/// <returns>An enumerable of <see cref="DamageState"/>.</returns>
public static IEnumerable<DamageState> AliveToDead()
{
foreach (DamageState state in Enum.GetValues(typeof(DamageState)))
{
if (state == DamageState.Invalid)
{
continue;
}
yield return state;
}
}
}
}

View File

@@ -1,29 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Mobs
{
[Serializable, NetSerializable]
public enum DamageStateVisuals
{
State
}
/// <summary>
/// Defines what state an <see cref="IEntity"/> is in.
///
/// Ordered from most alive to least alive.
/// To enumerate them in this way see
/// <see cref="DamageStateHelpers.AliveToDead"/>.
/// </summary>
[Serializable, NetSerializable]
public enum DamageState : byte
{
Invalid = 0,
Alive = 1,
Critical = 2,
Dead = 3
}
}

View File

@@ -1,28 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// Component required for a player to be able to examine things.
/// </summary>
[RegisterComponent]
public sealed class ExaminerComponent : Component
{
public override string Name => "Examiner";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("DoRangeCheck")]
private bool _doRangeCheck = true;
/// <summary>
/// Whether to do a distance check on examine.
/// If false, the user can theoretically examine from infinitely far away.
/// </summary>
public bool DoRangeCheck => _doRangeCheck;
}
}

View File

@@ -1,206 +0,0 @@
#nullable enable
using Content.Shared.Actions;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// An attempt to perform a specific action. Main purpose of this interface is to
/// reduce code duplication related to handling attempts to perform non-item vs item actions by
/// providing a single interface for various functionality that needs to be performed on both.
/// </summary>
public interface IActionAttempt
{
/// <summary>
/// Action Prototype attempting to be performed
/// </summary>
BaseActionPrototype Action { get; }
ComponentMessage PerformInstantActionMessage();
ComponentMessage PerformToggleActionMessage(bool on);
ComponentMessage PerformTargetPointActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args);
ComponentMessage PerformTargetEntityActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args);
/// <summary>
/// Tries to get the action state for this action from the actionsComponent, returning
/// true if found.
/// </summary>
bool TryGetActionState(SharedActionsComponent actionsComponent, out ActionState actionState);
/// <summary>
/// Toggles the action within the provided action component
/// </summary>
void ToggleAction(SharedActionsComponent actionsComponent, bool toggleOn);
/// <summary>
/// Perform the server-side logic of the action
/// </summary>
void DoInstantAction(IEntity player);
/// <summary>
/// Perform the server-side logic of the toggle action
/// </summary>
/// <returns>true if the attempt to toggle was successful, meaning the state should be toggled to the
/// indicated value</returns>
bool DoToggleAction(IEntity player, bool on);
/// <summary>
/// Perform the server-side logic of the target point action
/// </summary>
void DoTargetPointAction(IEntity player, EntityCoordinates target);
/// <summary>
/// Perform the server-side logic of the target entity action
/// </summary>
void DoTargetEntityAction(IEntity player, IEntity target);
}
public class ActionAttempt : IActionAttempt
{
private readonly ActionPrototype _action;
public BaseActionPrototype Action => _action;
public ActionAttempt(ActionPrototype action)
{
_action = action;
}
public bool TryGetActionState(SharedActionsComponent actionsComponent, out ActionState actionState)
{
return actionsComponent.TryGetActionState(_action.ActionType, out actionState);
}
public void ToggleAction(SharedActionsComponent actionsComponent, bool toggleOn)
{
actionsComponent.ToggleAction(_action.ActionType, toggleOn);
}
public void DoInstantAction(IEntity player)
{
_action.InstantAction.DoInstantAction(new InstantActionEventArgs(player, _action.ActionType));
}
public bool DoToggleAction(IEntity player, bool on)
{
return _action.ToggleAction.DoToggleAction(new ToggleActionEventArgs(player, _action.ActionType, on));
}
public void DoTargetPointAction(IEntity player, EntityCoordinates target)
{
_action.TargetPointAction.DoTargetPointAction(new TargetPointActionEventArgs(player, target, _action.ActionType));
}
public void DoTargetEntityAction(IEntity player, IEntity target)
{
_action.TargetEntityAction.DoTargetEntityAction(new TargetEntityActionEventArgs(player, _action.ActionType,
target));
}
public ComponentMessage PerformInstantActionMessage()
{
return new PerformInstantActionMessage(_action.ActionType);
}
public ComponentMessage PerformToggleActionMessage(bool toggleOn)
{
if (toggleOn)
{
return new PerformToggleOnActionMessage(_action.ActionType);
}
return new PerformToggleOffActionMessage(_action.ActionType);
}
public ComponentMessage PerformTargetPointActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args)
{
return new PerformTargetPointActionMessage(_action.ActionType, args.Coordinates);
}
public ComponentMessage PerformTargetEntityActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args)
{
return new PerformTargetEntityActionMessage(_action.ActionType, args.EntityUid);
}
public override string ToString()
{
return $"{nameof(_action)}: {_action.ActionType}";
}
}
public class ItemActionAttempt : IActionAttempt
{
private readonly ItemActionPrototype _action;
private readonly IEntity _item;
private readonly ItemActionsComponent _itemActions;
public BaseActionPrototype Action => _action;
public ItemActionAttempt(ItemActionPrototype action, IEntity item, ItemActionsComponent itemActions)
{
_action = action;
_item = item;
_itemActions = itemActions;
}
public void DoInstantAction(IEntity player)
{
_action.InstantAction.DoInstantAction(new InstantItemActionEventArgs(player, _item, _action.ActionType));
}
public bool DoToggleAction(IEntity player, bool on)
{
return _action.ToggleAction.DoToggleAction(new ToggleItemActionEventArgs(player, on, _item, _action.ActionType));
}
public void DoTargetPointAction(IEntity player, EntityCoordinates target)
{
_action.TargetPointAction.DoTargetPointAction(new TargetPointItemActionEventArgs(player, target, _item,
_action.ActionType));
}
public void DoTargetEntityAction(IEntity player, IEntity target)
{
_action.TargetEntityAction.DoTargetEntityAction(new TargetEntityItemActionEventArgs(player, target,
_item, _action.ActionType));
}
public bool TryGetActionState(SharedActionsComponent actionsComponent, out ActionState actionState)
{
return actionsComponent.TryGetItemActionState(_action.ActionType, _item, out actionState);
}
public void ToggleAction(SharedActionsComponent actionsComponent, bool toggleOn)
{
_itemActions.Toggle(_action.ActionType, toggleOn);
}
public ComponentMessage PerformInstantActionMessage()
{
return new PerformInstantItemActionMessage(_action.ActionType, _item.Uid);
}
public ComponentMessage PerformToggleActionMessage(bool toggleOn)
{
if (toggleOn)
{
return new PerformToggleOnItemActionMessage(_action.ActionType, _item.Uid);
}
return new PerformToggleOffItemActionMessage(_action.ActionType, _item.Uid);
}
public ComponentMessage PerformTargetPointActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args)
{
return new PerformTargetPointItemActionMessage(_action.ActionType, _item.Uid, args.Coordinates);
}
public ComponentMessage PerformTargetEntityActionMessage(PointerInputCmdHandler.PointerInputCmdArgs args)
{
return new PerformTargetEntityItemActionMessage(_action.ActionType, _item.Uid, args.EntityUid);
}
public override string ToString()
{
return $"{nameof(_action)}: {_action.ActionType}, {nameof(_item)}: {_item}";
}
}
}

View File

@@ -1,251 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Actions;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// This should be used on items which provide actions. Defines which actions the item provides
/// and allows modifying the states of those actions. Item components should use this rather than
/// SharedActionsComponent on the player to handle granting / revoking / modifying the states of the
/// actions provided by this item.
///
/// When a player equips this item, all the actions defined in this component will be granted to the
/// player in their current states. This means the states will persist between players.
///
/// Currently only maintained server side and not synced to client, as are all the equip/unequip events.
/// </summary>
[RegisterComponent]
public class ItemActionsComponent : Component, IEquippedHand, IEquipped, IUnequipped, IUnequippedHand
{
public override string Name => "ItemActions";
/// <summary>
/// Configuration for the item actions initially provided by this item. Actions defined here
/// will be automatically granted unless their state is modified using the methods
/// on this component. Additional actions can be granted by this item via GrantOrUpdate
/// </summary>
public IEnumerable<ItemActionConfig> ActionConfigs => _actionConfigs;
public bool IsEquipped => InSlot != EquipmentSlotDefines.Slots.NONE || InHand != null;
/// <summary>
/// Slot currently equipped to, NONE if not equipped to an equip slot.
/// </summary>
public EquipmentSlotDefines.Slots InSlot { get; private set; }
/// <summary>
/// hand it's currently in, null if not in a hand.
/// </summary>
public SharedHand? InHand { get; private set; }
/// <summary>
/// Entity currently holding this in hand or equip slot. Null if not held.
/// </summary>
public IEntity? Holder { get; private set; }
// cached actions component of the holder, since we'll need to access it frequently
private SharedActionsComponent? _holderActionsComponent;
[DataField("actions")]
private List<ItemActionConfig> _actionConfigs
{
get => internalActionConfigs;
set
{
internalActionConfigs = value;
foreach (var actionConfig in value)
{
GrantOrUpdate(actionConfig.ActionType, actionConfig.Enabled, false, null);
}
}
}
// State of all actions provided by this item.
private readonly Dictionary<ItemActionType, ActionState> _actions = new();
private List<ItemActionConfig> internalActionConfigs = new ();
protected override void Startup()
{
base.Startup();
GrantOrUpdateAllToHolder();
}
protected override void Shutdown()
{
base.Shutdown();
RevokeAllFromHolder();
}
private void GrantOrUpdateAllToHolder()
{
if (_holderActionsComponent == null) return;
foreach (var (actionType, state) in _actions)
{
_holderActionsComponent.GrantOrUpdateItemAction(actionType, Owner.Uid, state);
}
}
private void RevokeAllFromHolder()
{
if (_holderActionsComponent == null) return;
foreach (var (actionType, state) in _actions)
{
_holderActionsComponent.RevokeItemAction(actionType, Owner.Uid);
}
}
/// <summary>
/// Update the state of the action, granting it if it isn't already granted.
/// If the action had any existing state, those specific fields will be overwritten by any
/// corresponding non-null arguments.
/// </summary>
/// <param name="actionType">action being granted / updated</param>
/// <param name="enabled">When null, preserves the current enable status of the action, defaulting
/// to true if action has no current state.
/// When non-null, indicates whether the entity is able to perform the action (if disabled,
/// the player will see they have the action but it will appear greyed out)</param>
/// <param name="toggleOn">When null, preserves the current toggle status of the action, defaulting
/// to false if action has no current state.
/// When non-null, action will be shown toggled to this value</param>
/// <param name="cooldown"> When null (unless clearCooldown is true), preserves the current cooldown status of the action, defaulting
/// to no cooldown if action has no current state.
/// When non-null or clearCooldown is true, action cooldown will be set to this value. Note that this cooldown
/// is tied to this item.</param>
/// <param name="clearCooldown"> If true, setting cooldown to null will clear the current cooldown
/// of this action rather than preserving it.</param>
public void GrantOrUpdate(ItemActionType actionType, bool? enabled = null,
bool? toggleOn = null,
(TimeSpan start, TimeSpan end)? cooldown = null, bool clearCooldown = false)
{
var dirty = false;
// this will be overwritten if we find the value in our dict, otherwise
// we will use this as our new action state.
if (!_actions.TryGetValue(actionType, out var actionState))
{
dirty = true;
actionState = new ActionState(enabled ?? true, toggleOn ?? false);
}
if (enabled.HasValue && enabled != actionState.Enabled)
{
dirty = true;
actionState.Enabled = true;
}
if ((cooldown.HasValue || clearCooldown) && actionState.Cooldown != cooldown)
{
dirty = true;
actionState.Cooldown = cooldown;
}
if (toggleOn.HasValue && actionState.ToggledOn != toggleOn.Value)
{
dirty = true;
actionState.ToggledOn = toggleOn.Value;
}
if (!dirty) return;
_actions[actionType] = actionState;
_holderActionsComponent?.GrantOrUpdateItemAction(actionType, Owner.Uid, actionState);
}
/// <summary>
/// Update the cooldown of a particular action. Actions on cooldown cannot be used.
/// Setting the cooldown to null clears it.
/// </summary>
public void Cooldown(ItemActionType actionType, (TimeSpan start, TimeSpan end)? cooldown = null)
{
GrantOrUpdate(actionType, cooldown: cooldown, clearCooldown: true);
}
/// <summary>
/// Enable / disable this action. Disabled actions are still shown to the player, but
/// shown as not usable.
/// </summary>
public void SetEnabled(ItemActionType actionType, bool enabled)
{
GrantOrUpdate(actionType, enabled);
}
/// <summary>
/// Toggle the action on / off
/// </summary>
public void Toggle(ItemActionType actionType, bool toggleOn)
{
GrantOrUpdate(actionType, toggleOn: toggleOn);
}
void IEquippedHand.EquippedHand(EquippedHandEventArgs eventArgs)
{
// this entity cannot be granted actions if no actions component
if (!eventArgs.User.TryGetComponent<SharedActionsComponent>(out var actionsComponent))
return;
Holder = eventArgs.User;
_holderActionsComponent = actionsComponent;
InSlot = EquipmentSlotDefines.Slots.NONE;
InHand = eventArgs.Hand;
GrantOrUpdateAllToHolder();
}
void IEquipped.Equipped(EquippedEventArgs eventArgs)
{
// this entity cannot be granted actions if no actions component
if (!eventArgs.User.TryGetComponent<SharedActionsComponent>(out var actionsComponent))
return;
Holder = eventArgs.User;
_holderActionsComponent = actionsComponent;
InSlot = eventArgs.Slot;
InHand = null;
GrantOrUpdateAllToHolder();
}
void IUnequipped.Unequipped(UnequippedEventArgs eventArgs)
{
RevokeAllFromHolder();
Holder = null;
_holderActionsComponent = null;
InSlot = EquipmentSlotDefines.Slots.NONE;
InHand = null;
}
void IUnequippedHand.UnequippedHand(UnequippedHandEventArgs eventArgs)
{
RevokeAllFromHolder();
Holder = null;
_holderActionsComponent = null;
InSlot = EquipmentSlotDefines.Slots.NONE;
InHand = null;
}
}
/// <summary>
/// Configuration for an item action provided by an item.
/// </summary>
[DataDefinition]
public class ItemActionConfig : ISerializationHooks
{
[DataField("actionType", required: true)]
public ItemActionType ActionType { get; private set; } = ItemActionType.Error;
/// <summary>
/// Whether action is initially enabled on this item. Defaults to true.
/// </summary>
public bool Enabled { get; private set; } = true;
void ISerializationHooks.AfterDeserialization()
{
if (ActionType == ItemActionType.Error)
{
Logger.ErrorS("action", "invalid or missing actionType");
}
}
}
}

View File

@@ -1,653 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Actions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// Manages the actions available to an entity.
/// Should only be used for player-controlled entities.
///
/// Actions are granted directly to the owner entity. Item actions are granted via a particular item which
/// must be in the owner's inventory (the action is revoked when item leaves the owner's inventory). This
/// should almost always be done via ItemActionsComponent on the item entity (which also tracks the
/// cooldowns associated with the actions on that item).
///
/// Actions can still have an associated state even when revoked. For example, a flashlight toggle action
/// may be unusable while the player is stunned, but this component will still have an entry for the action
/// so the user can see whether it's currently toggled on or off.
/// </summary>
public abstract class SharedActionsComponent : Component
{
private static readonly TimeSpan CooldownExpiryThreshold = TimeSpan.FromSeconds(10);
[Dependency]
protected readonly ActionManager ActionManager = default!;
[Dependency]
protected readonly IGameTiming GameTiming = default!;
[Dependency]
protected readonly IEntityManager EntityManager = default!;
public override string Name => "Actions";
public override uint? NetID => ContentNetIDs.ACTIONS;
/// <summary>
/// Actions granted to this entity as soon as they spawn, regardless
/// of the status of the entity.
/// </summary>
public IEnumerable<ActionType> InnateActions => _innateActions ?? Enumerable.Empty<ActionType>();
[DataField("innateActions")]
private List<ActionType>? _innateActions = null;
// entries are removed from this if they are at the initial state (not enabled, no cooldown, toggled off).
// a system runs which periodically removes cooldowns from entries when they are revoked and their
// cooldowns have expired for a long enough time, also removing the entry if it is then at initial state.
// This helps to keep our component state smaller.
[ViewVariables]
private Dictionary<ActionType, ActionState> _actions = new();
// Holds item action states. Item actions are only added to this when granted, and are removed
// when revoked or when they leave inventory. This is almost entirely handled by ItemActionsComponent on
// item entities.
[ViewVariables]
private Dictionary<EntityUid, Dictionary<ItemActionType, ActionState>> _itemActions =
new();
protected override void Startup()
{
foreach (var actionType in InnateActions)
{
Grant(actionType);
}
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new ActionComponentState(_actions, _itemActions);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not ActionComponentState state)
{
return;
}
_actions = state.Actions;
_itemActions = state.ItemActions;
}
/// <summary>
/// Gets the action state associated with the specified action type, if it has been
/// granted, has a cooldown, or has been toggled on
/// </summary>
/// <returns>false if not found for this action type</returns>
public bool TryGetActionState(ActionType actionType, out ActionState actionState)
{
return _actions.TryGetValue(actionType, out actionState);
}
/// <summary>
/// Gets the item action states associated with the specified item if any have been granted
/// and not yet revoked.
/// </summary>
/// <returns>false if no states found for this item action type.</returns>
public bool TryGetItemActionStates(EntityUid item, [NotNullWhen((true))] out IReadOnlyDictionary<ItemActionType, ActionState>? itemActionStates)
{
if (_itemActions.TryGetValue(item, out var actualItemActionStates))
{
itemActionStates = actualItemActionStates;
return true;
}
itemActionStates = null;
return false;
}
/// <seealso cref="TryGetItemActionStates(Robust.Shared.GameObjects.EntityUid,out System.Collections.Generic.IReadOnlyDictionary{Content.Shared.Actions.ItemActionType,Content.Shared.GameObjects.Components.Mobs.ActionState}?)"/>
public bool TryGetItemActionStates(IEntity item,
[NotNullWhen((true))] out IReadOnlyDictionary<ItemActionType, ActionState>? itemActionStates)
{
return TryGetItemActionStates(item.Uid, out itemActionStates);
}
/// <summary>
/// Gets the item action state associated with the specified item action type for the specified item, if it has any.
/// </summary>
/// <returns>false if no state found for this item action type for this item</returns>
public bool TryGetItemActionState(ItemActionType actionType, EntityUid item, out ActionState actionState)
{
if (_itemActions.TryGetValue(item, out var actualItemActionStates))
{
return actualItemActionStates.TryGetValue(actionType, out actionState);
}
actionState = default;
return false;
}
/// <returns>true if the action is granted and enabled (if item action, if granted and enabled for any item)</returns>
public bool IsGranted(BaseActionPrototype actionType)
{
return actionType switch
{
ActionPrototype actionPrototype => IsGranted(actionPrototype.ActionType),
ItemActionPrototype itemActionPrototype => IsGranted(itemActionPrototype.ActionType),
_ => false
};
}
public bool IsGranted(ActionType actionType)
{
if (TryGetActionState(actionType, out var actionState))
{
return actionState.Enabled;
}
return false;
}
/// <returns>true if the action is granted and enabled for any item. This
/// has to traverse the entire item state dictionary so please avoid frequent calls.</returns>
public bool IsGranted(ItemActionType actionType)
{
return _itemActions.Values.SelectMany(vals => vals)
.Any(state => state.Key == actionType && state.Value.Enabled);
}
/// <seealso cref="TryGetItemActionState(Content.Shared.Actions.ItemActionType,Robust.Shared.GameObjects.EntityUid,out Content.Shared.GameObjects.Components.Mobs.ActionState)"/>
public bool TryGetItemActionState(ItemActionType actionType, IEntity item, out ActionState actionState)
{
return TryGetItemActionState(actionType, item.Uid, out actionState);
}
/// <summary>
/// Gets all action types that have non-initial state (granted, have a cooldown, or toggled on).
/// </summary>
public IReadOnlyDictionary<ActionType, ActionState> ActionStates()
{
return _actions;
}
/// <summary>
/// Gets all items that have actions currently granted (that are not revoked
/// and still in inventory).
/// Map from item uid -> (action type -> associated action state)
/// PLEASE DO NOT MODIFY THE INNER DICTIONARY! I CANNOT CAST IT TO IReadOnlyDictionary!
/// </summary>
public IReadOnlyDictionary<EntityUid,Dictionary<ItemActionType, ActionState>> ItemActionStates()
{
return _itemActions;
}
/// <summary>
/// Creates or updates the action state with the supplied non-null values
/// </summary>
private void GrantOrUpdate(ActionType actionType, bool? enabled = null, bool? toggleOn = null,
(TimeSpan start, TimeSpan end)? cooldown = null, bool clearCooldown = false)
{
var dirty = false;
if (!_actions.TryGetValue(actionType, out var actionState))
{
// no state at all for this action, create it anew
dirty = true;
actionState = new ActionState(enabled ?? false, toggleOn ?? false);
}
if (enabled.HasValue && actionState.Enabled != enabled.Value)
{
dirty = true;
actionState.Enabled = enabled.Value;
}
if ((cooldown.HasValue || clearCooldown) && actionState.Cooldown != cooldown)
{
dirty = true;
actionState.Cooldown = cooldown;
}
if (toggleOn.HasValue && actionState.ToggledOn != toggleOn.Value)
{
dirty = true;
actionState.ToggledOn = toggleOn.Value;
}
if (!dirty) return;
_actions[actionType] = actionState;
AfterActionChanged();
Dirty();
}
/// <summary>
/// Intended to only be used by ItemActionsComponent.
/// Updates the state of the item action provided by the item, granting the action
/// if it is not yet granted to the player. Should be called whenever the
/// status changes. The existing state will be completely overwritten by the new state.
/// </summary>
public void GrantOrUpdateItemAction(ItemActionType actionType, EntityUid item, ActionState state)
{
if (!_itemActions.TryGetValue(item, out var itemStates))
{
itemStates = new Dictionary<ItemActionType, ActionState>();
_itemActions[item] = itemStates;
}
itemStates[actionType] = state;
AfterActionChanged();
Dirty();
}
/// <summary>
/// Intended to only be used by ItemActionsComponent. Revokes the item action so the player no longer
/// sees it and can no longer use it.
/// </summary>
public void RevokeItemAction(ItemActionType actionType, EntityUid item)
{
if (!_itemActions.TryGetValue(item, out var itemStates))
return;
itemStates.Remove(actionType);
if (itemStates.Count == 0)
{
_itemActions.Remove(item);
}
AfterActionChanged();
Dirty();
}
/// <summary>
/// Grants the entity the ability to perform the action, optionally overriding its
/// current state with specified values.
///
/// Even if the action was already granted, if the action had any state (cooldown, toggle) prior to this method
/// being called, it will be preserved, with specific fields optionally overridden by any of the provided
/// non-null arguments.
/// </summary>
/// <param name="toggleOn">When null, preserves the current toggle status of the action, defaulting
/// to false if action has no current state.
/// When non-null, action will be shown toggled to this value</param>
/// <param name="cooldown"> When null, preserves the current cooldown status of the action, defaulting
/// to no cooldown if action has no current state.
/// When non-null, action cooldown will be set to this value.</param>
public void Grant(ActionType actionType, bool? toggleOn = null,
(TimeSpan start, TimeSpan end)? cooldown = null)
{
GrantOrUpdate(actionType, true, toggleOn, cooldown);
}
/// <summary>
/// Grants the entity the ability to perform the action, resetting its state
/// to its initial state and settings its state based on supplied parameters.
///
/// Even if the action was already granted, if the action had any state (cooldown, toggle) prior to this method
/// being called, it will be reset to initial (no cooldown, toggled off).
/// </summary>
/// <param name="toggleOn">action will be shown toggled to this value</param>
/// <param name="cooldown">action cooldown will be set to this value (by default the cooldown is cleared).</param>
public void GrantFromInitialState(ActionType actionType, bool toggleOn = false,
(TimeSpan start, TimeSpan end)? cooldown = null)
{
_actions.Remove(actionType);
Grant(actionType, toggleOn, cooldown);
}
/// <summary>
/// Sets the cooldown for the action. Actions on cooldown cannot be used.
///
/// This will work even if the action is revoked -
/// for example if there's an ability with a cooldown which is temporarily unusable due
/// to the player being stunned, the cooldown will still tick down even while the player
/// is stunned.
///
/// Setting cooldown to null clears it.
/// </summary>
public void Cooldown(ActionType actionType, (TimeSpan start, TimeSpan end)? cooldown)
{
GrantOrUpdate(actionType, cooldown: cooldown, clearCooldown: true);
}
/// <summary>
/// Revokes the ability to perform the action for this entity. Current state
/// of the action (toggle / cooldown) is preserved.
/// </summary>
public void Revoke(ActionType actionType)
{
if (!_actions.TryGetValue(actionType, out var actionState)) return;
if (!actionState.Enabled) return;
actionState.Enabled = false;
// don't store it anymore if its at its initial state.
if (actionState.IsAtInitialState)
{
_actions.Remove(actionType);
}
else
{
_actions[actionType] = actionState;
}
AfterActionChanged();
Dirty();
}
/// <summary>
/// Toggles the action to the specified value. Works even if the action is on cooldown
/// or revoked.
/// </summary>
public void ToggleAction(ActionType actionType, bool toggleOn)
{
Grant(actionType, toggleOn);
}
/// <summary>
/// Clears any cooldowns which have expired beyond the predefined threshold.
/// this should be run periodically to ensure we don't have unbounded growth of
/// our saved action data, and keep our component state sent to the client as minimal as possible.
/// </summary>
public void ExpireCooldowns()
{
// actions - only clear cooldowns and remove associated action state
// if the action is at initial state
var actionTypesToRemove = new List<ActionType>();
foreach (var (actionType, actionState) in _actions)
{
// ignore it unless we may be able to delete it due to
// clearing the cooldown
if (!actionState.IsAtInitialStateExceptCooldown) continue;
if (!actionState.Cooldown.HasValue)
{
actionTypesToRemove.Add(actionType);
continue;
}
var expiryTime = GameTiming.CurTime - actionState.Cooldown.Value.Item2;
if (expiryTime > CooldownExpiryThreshold)
{
actionTypesToRemove.Add(actionType);
}
}
foreach (var remove in actionTypesToRemove)
{
_actions.Remove(remove);
}
}
/// <summary>
/// Invoked after a change has been made to an action state in this component.
/// </summary>
protected virtual void AfterActionChanged() { }
}
[Serializable, NetSerializable]
public class ActionComponentState : ComponentState
{
public Dictionary<ActionType, ActionState> Actions;
public Dictionary<EntityUid, Dictionary<ItemActionType, ActionState>> ItemActions;
public ActionComponentState(Dictionary<ActionType, ActionState> actions,
Dictionary<EntityUid, Dictionary<ItemActionType, ActionState>> itemActions) : base(ContentNetIDs.ACTIONS)
{
Actions = actions;
ItemActions = itemActions;
}
}
[Serializable, NetSerializable]
public struct ActionState
{
/// <summary>
/// False if this action is not currently allowed to be performed.
/// </summary>
public bool Enabled;
/// <summary>
/// Only used for toggle actions, indicates whether it's currently toggled on or off
/// TODO: Eventually this should probably be a byte so we it can toggle through multiple states.
/// </summary>
public bool ToggledOn;
public (TimeSpan start, TimeSpan end)? Cooldown;
public bool IsAtInitialState => IsAtInitialStateExceptCooldown && !Cooldown.HasValue;
public bool IsAtInitialStateExceptCooldown => !Enabled && !ToggledOn;
/// <summary>
/// Creates an action state for the indicated type, defaulting to the
/// initial state.
/// </summary>
public ActionState(bool enabled = false, bool toggledOn = false, (TimeSpan start, TimeSpan end)? cooldown = null)
{
Enabled = enabled;
ToggledOn = toggledOn;
Cooldown = cooldown;
}
public bool IsOnCooldown(TimeSpan curTime)
{
if (Cooldown == null) return false;
return curTime < Cooldown.Value.Item2;
}
public bool IsOnCooldown(IGameTiming gameTiming)
{
return IsOnCooldown(gameTiming.CurTime);
}
public bool Equals(ActionState other)
{
return Enabled == other.Enabled && ToggledOn == other.ToggledOn && Nullable.Equals(Cooldown, other.Cooldown);
}
public override bool Equals(object? obj)
{
return obj is ActionState other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(Enabled, ToggledOn, Cooldown);
}
}
[Serializable, NetSerializable]
public abstract class BasePerformActionMessage : ComponentMessage
{
public abstract BehaviorType BehaviorType { get; }
}
[Serializable, NetSerializable]
public abstract class PerformActionMessage : BasePerformActionMessage
{
public readonly ActionType ActionType;
protected PerformActionMessage(ActionType actionType)
{
Directed = true;
ActionType = actionType;
}
}
[Serializable, NetSerializable]
public abstract class PerformItemActionMessage : BasePerformActionMessage
{
public readonly ItemActionType ActionType;
public readonly EntityUid Item;
protected PerformItemActionMessage(ItemActionType actionType, EntityUid item)
{
Directed = true;
ActionType = actionType;
Item = item;
}
}
/// <summary>
/// A message that tells server we want to run the instant action logic.
/// </summary>
[Serializable, NetSerializable]
public class PerformInstantActionMessage : PerformActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Instant;
public PerformInstantActionMessage(ActionType actionType) : base(actionType)
{
}
}
/// <summary>
/// A message that tells server we want to run the instant action logic.
/// </summary>
[Serializable, NetSerializable]
public class PerformInstantItemActionMessage : PerformItemActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Instant;
public PerformInstantItemActionMessage(ItemActionType actionType, EntityUid item) : base(actionType, item)
{
}
}
public interface IToggleActionMessage
{
bool ToggleOn { get; }
}
public interface ITargetPointActionMessage
{
/// <summary>
/// Targeted local coordinates
/// </summary>
EntityCoordinates Target { get; }
}
public interface ITargetEntityActionMessage
{
/// <summary>
/// Targeted entity
/// </summary>
EntityUid Target { get; }
}
/// <summary>
/// A message that tells server we want to toggle on the indicated action.
/// </summary>
[Serializable, NetSerializable]
public class PerformToggleOnActionMessage : PerformActionMessage, IToggleActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Toggle;
public bool ToggleOn => true;
public PerformToggleOnActionMessage(ActionType actionType) : base(actionType) { }
}
/// <summary>
/// A message that tells server we want to toggle off the indicated action.
/// </summary>
[Serializable, NetSerializable]
public class PerformToggleOffActionMessage : PerformActionMessage, IToggleActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Toggle;
public bool ToggleOn => false;
public PerformToggleOffActionMessage(ActionType actionType) : base(actionType) { }
}
/// <summary>
/// A message that tells server we want to toggle on the indicated action.
/// </summary>
[Serializable, NetSerializable]
public class PerformToggleOnItemActionMessage : PerformItemActionMessage, IToggleActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Toggle;
public bool ToggleOn => true;
public PerformToggleOnItemActionMessage(ItemActionType actionType, EntityUid item) : base(actionType, item) { }
}
/// <summary>
/// A message that tells server we want to toggle off the indicated action.
/// </summary>
[Serializable, NetSerializable]
public class PerformToggleOffItemActionMessage : PerformItemActionMessage, IToggleActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.Toggle;
public bool ToggleOn => false;
public PerformToggleOffItemActionMessage(ItemActionType actionType, EntityUid item) : base(actionType, item) { }
}
/// <summary>
/// A message that tells server we want to target the provided point with a particular action.
/// </summary>
[Serializable, NetSerializable]
public class PerformTargetPointActionMessage : PerformActionMessage, ITargetPointActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.TargetPoint;
private readonly EntityCoordinates _target;
public EntityCoordinates Target => _target;
public PerformTargetPointActionMessage(ActionType actionType, EntityCoordinates target) : base(actionType)
{
_target = target;
}
}
/// <summary>
/// A message that tells server we want to target the provided point with a particular action.
/// </summary>
[Serializable, NetSerializable]
public class PerformTargetPointItemActionMessage : PerformItemActionMessage, ITargetPointActionMessage
{
private readonly EntityCoordinates _target;
public EntityCoordinates Target => _target;
public override BehaviorType BehaviorType => BehaviorType.TargetPoint;
public PerformTargetPointItemActionMessage(ItemActionType actionType, EntityUid item, EntityCoordinates target) : base(actionType, item)
{
_target = target;
}
}
/// <summary>
/// A message that tells server we want to target the provided entity with a particular action.
/// </summary>
[Serializable, NetSerializable]
public class PerformTargetEntityActionMessage : PerformActionMessage, ITargetEntityActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.TargetEntity;
private readonly EntityUid _target;
public EntityUid Target => _target;
public PerformTargetEntityActionMessage(ActionType actionType, EntityUid target) : base(actionType)
{
_target = target;
}
}
/// <summary>
/// A message that tells server we want to target the provided entity with a particular action.
/// </summary>
[Serializable, NetSerializable]
public class PerformTargetEntityItemActionMessage : PerformItemActionMessage, ITargetEntityActionMessage
{
public override BehaviorType BehaviorType => BehaviorType.TargetEntity;
private readonly EntityUid _target;
public EntityUid Target => _target;
public PerformTargetEntityItemActionMessage(ItemActionType actionType, EntityUid item, EntityUid target) : base(actionType, item)
{
_target = target;
}
}
}

View File

@@ -1,199 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Shared.Alert;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Mobs
{
/// <summary>
/// Handles the icons on the right side of the screen.
/// Should only be used for player-controlled entities.
/// </summary>
public abstract class SharedAlertsComponent : Component
{
[Dependency]
protected readonly AlertManager AlertManager = default!;
public override string Name => "Alerts";
public override uint? NetID => ContentNetIDs.ALERTS;
[ViewVariables] private Dictionary<AlertKey, AlertState> _alerts = new();
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not AlertsComponentState state)
{
return;
}
_alerts = state.Alerts;
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new AlertsComponentState(_alerts);
}
/// <returns>true iff an alert of the indicated alert category is currently showing</returns>
public bool IsShowingAlertCategory(AlertCategory alertCategory)
{
return IsShowingAlert(AlertKey.ForCategory(alertCategory));
}
/// <returns>true iff an alert of the indicated id is currently showing</returns>
public bool IsShowingAlert(AlertType alertType)
{
if (AlertManager.TryGet(alertType, out var alert))
{
return IsShowingAlert(alert.AlertKey);
}
Logger.DebugS("alert", "unknown alert type {0}", alertType);
return false;
}
/// <returns>true iff an alert of the indicated key is currently showing</returns>
protected bool IsShowingAlert(AlertKey alertKey)
{
return _alerts.ContainsKey(alertKey);
}
protected IEnumerable<KeyValuePair<AlertKey, AlertState>> EnumerateAlertStates()
{
return _alerts;
}
protected bool TryGetAlertState(AlertKey key, out AlertState alertState)
{
return _alerts.TryGetValue(key, out alertState);
}
/// <summary>
/// Shows the alert. If the alert or another alert of the same category is already showing,
/// it will be updated / replaced with the specified values.
/// </summary>
/// <param name="alertType">type of the alert to set</param>
/// <param name="severity">severity, if supported by the alert</param>
/// <param name="cooldown">cooldown start and end, if null there will be no cooldown (and it will
/// be erased if there is currently a cooldown for the alert)</param>
public void ShowAlert(AlertType alertType, short? severity = null, ValueTuple<TimeSpan, TimeSpan>? cooldown = null)
{
if (AlertManager.TryGet(alertType, out var alert))
{
if (_alerts.TryGetValue(alert.AlertKey, out var alertStateCallback) &&
alert.AlertType == alertType &&
alertStateCallback.Severity == severity && alertStateCallback.Cooldown == cooldown)
{
return;
}
// In the case we're changing the alert type but not the category, we need to remove it first.
_alerts.Remove(alert.AlertKey);
_alerts[alert.AlertKey] = new AlertState
{Cooldown = cooldown, Severity = severity};
AfterShowAlert();
Dirty();
}
else
{
Logger.ErrorS("alert", "Unable to show alert {0}, please ensure this alertType has" +
" a corresponding YML alert prototype",
alertType);
}
}
/// <summary>
/// Clear the alert with the given category, if one is currently showing.
/// </summary>
public void ClearAlertCategory(AlertCategory category)
{
var key = AlertKey.ForCategory(category);
if (!_alerts.Remove(key))
{
return;
}
AfterClearAlert();
Dirty();
}
/// <summary>
/// Clear the alert of the given type if it is currently showing.
/// </summary>
public void ClearAlert(AlertType alertType)
{
if (AlertManager.TryGet(alertType, out var alert))
{
if (!_alerts.Remove(alert.AlertKey))
{
return;
}
AfterClearAlert();
Dirty();
}
else
{
Logger.ErrorS("alert", "unable to clear alert, unknown alertType {0}", alertType);
}
}
/// <summary>
/// Invoked after showing an alert prior to dirtying the component
/// </summary>
protected virtual void AfterShowAlert() { }
/// <summary>
/// Invoked after clearing an alert prior to dirtying the component
/// </summary>
protected virtual void AfterClearAlert() { }
}
[Serializable, NetSerializable]
public class AlertsComponentState : ComponentState
{
public Dictionary<AlertKey, AlertState> Alerts;
public AlertsComponentState(Dictionary<AlertKey, AlertState> alerts) : base(ContentNetIDs.ALERTS)
{
Alerts = alerts;
}
}
/// <summary>
/// A message that calls the click interaction on a alert
/// </summary>
[Serializable, NetSerializable]
public class ClickAlertMessage : ComponentMessage
{
public readonly AlertType AlertType;
public ClickAlertMessage(AlertType alertType)
{
Directed = true;
AlertType = alertType;
}
}
[Serializable, NetSerializable]
public struct AlertState
{
public short? Severity;
public ValueTuple<TimeSpan, TimeSpan>? Cooldown;
}
}

View File

@@ -1,29 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Mobs
{
public abstract class SharedCameraRecoilComponent : Component
{
public sealed override string Name => "CameraRecoil";
public override uint? NetID => ContentNetIDs.CAMERA_RECOIL;
public abstract void Kick(Vector2 recoil);
[Serializable, NetSerializable]
protected class RecoilKickMessage : ComponentMessage
{
public readonly Vector2 Recoil;
public RecoilKickMessage(Vector2 recoil)
{
Directed = true;
Recoil = recoil;
}
}
}
}

View File

@@ -1,86 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Mobs
{
public abstract class SharedCombatModeComponent : Component
{
public sealed override uint? NetID => ContentNetIDs.COMBATMODE;
public override string Name => "CombatMode";
private bool _isInCombatMode;
private TargetingZone _activeZone;
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool IsInCombatMode
{
get => _isInCombatMode;
set
{
if (_isInCombatMode == value) return;
_isInCombatMode = value;
Dirty();
// Regenerate physics contacts -> Can probably just selectively check
/* Still a bit jank so left disabled for now.
if (Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
{
if (value)
{
physicsComponent.WakeBody();
}
physicsComponent.RegenerateContacts();
}
*/
}
}
[ViewVariables(VVAccess.ReadWrite)]
public virtual TargetingZone ActiveZone
{
get => _activeZone;
set
{
if (_activeZone == value) return;
_activeZone = value;
Dirty();
}
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not CombatModeComponentState state)
return;
IsInCombatMode = state.IsInCombatMode;
ActiveZone = state.TargetingZone;
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new CombatModeComponentState(IsInCombatMode, ActiveZone);
}
[Serializable, NetSerializable]
protected sealed class CombatModeComponentState : ComponentState
{
public bool IsInCombatMode { get; }
public TargetingZone TargetingZone { get; }
public CombatModeComponentState(bool isInCombatMode, TargetingZone targetingZone)
: base(ContentNetIDs.COMBATMODE)
{
IsInCombatMode = isInCombatMode;
TargetingZone = targetingZone;
}
}
}
}

View File

@@ -1,121 +0,0 @@
#nullable enable
using System;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Appearance;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Mobs
{
public abstract class SharedHumanoidAppearanceComponent : Component
{
private HumanoidCharacterAppearance _appearance = HumanoidCharacterAppearance.Default();
private Sex _sex;
private Gender _gender;
public sealed override string Name => "HumanoidAppearance";
public sealed override uint? NetID => ContentNetIDs.HUMANOID_APPEARANCE;
[DataField("categoriesHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesHair { get; set; } = SpriteAccessoryCategories.HumanHair;
[DataField("categoriesFacialHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesFacialHair { get; set; } = SpriteAccessoryCategories.HumanFacialHair;
[ViewVariables]
[DataField("canColorHair")]
public bool CanColorHair { get; set; } = true;
[ViewVariables]
[DataField("canColorFacialHair")]
public bool CanColorFacialHair { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public virtual HumanoidCharacterAppearance Appearance
{
get => _appearance;
set
{
_appearance = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public virtual Sex Sex
{
get => _sex;
set
{
_sex = value;
Dirty();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public virtual Gender Gender
{
get => _gender;
set
{
_gender = value;
if (Owner.TryGetComponent(out GrammarComponent? g))
{
g.Gender = value;
}
Dirty();
}
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new HumanoidAppearanceComponentState(Appearance, Sex, Gender);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
base.HandleComponentState(curState, nextState);
if (curState is not HumanoidAppearanceComponentState cast)
return;
Appearance = cast.Appearance;
Sex = cast.Sex;
Gender = cast.Gender;
}
public void UpdateFromProfile(ICharacterProfile profile)
{
var humanoid = (HumanoidCharacterProfile) profile;
Appearance = (HumanoidCharacterAppearance) humanoid.CharacterAppearance;
Sex = humanoid.Sex;
Gender = humanoid.Gender;
}
[Serializable]
[NetSerializable]
private sealed class HumanoidAppearanceComponentState : ComponentState
{
public HumanoidAppearanceComponentState(HumanoidCharacterAppearance appearance, Sex sex, Gender gender) :
base(ContentNetIDs.HUMANOID_APPEARANCE)
{
Appearance = appearance;
Sex = sex;
Gender = gender;
}
public HumanoidCharacterAppearance Appearance { get; }
public Sex Sex { get; }
public Gender Gender { get; }
}
}
}

Some files were not shown because too many files have changed in this diff Show More