Re-organize all projects (#4166)
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using Content.Shared.NetIDs;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Atmos.GasTank
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#nullable enable
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Construction
|
||||
{
|
||||
[NetSerializable]
|
||||
public enum MachineFrameVisuals
|
||||
{
|
||||
State,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#nullable enable
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Items
|
||||
{
|
||||
public interface ISharedHandsComponent : IComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.MachineLinking
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum SignalSwitchVisuals
|
||||
{
|
||||
On
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Mining
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum AsteroidRockVisuals
|
||||
{
|
||||
State
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user