Merge branch 'master-upstream' into expl_int_analyzer
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
{
|
||||
Error,
|
||||
HumanScream,
|
||||
Disarm,
|
||||
DebugInstant,
|
||||
DebugToggle,
|
||||
DebugTargetPoint,
|
||||
@@ -23,6 +24,7 @@
|
||||
Error,
|
||||
ToggleInternals,
|
||||
ToggleLight,
|
||||
ToggleMagboots,
|
||||
DebugInstant,
|
||||
DebugToggle,
|
||||
DebugTargetPoint,
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace Content.Shared.Actions
|
||||
[ViewVariables]
|
||||
public SpriteSpecifier IconOn { get; private set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Name to show in UI. Accepts formatting.
|
||||
/// </summary>
|
||||
@@ -60,6 +58,18 @@ namespace Content.Shared.Actions
|
||||
/// </summary>
|
||||
public bool Repeat { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For TargetEntity/TargetPoint actions, should the action be de-selected if currently selected (choosing a target)
|
||||
/// when it goes on cooldown. Defaults to false.
|
||||
/// </summary>
|
||||
public bool DeselectOnCooldown { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For TargetEntity actions, should the action be de-selected if the user doesn't click an entity when
|
||||
/// selecting a target. Defaults to false.
|
||||
/// </summary>
|
||||
public bool DeselectWhenEntityNotClicked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filters that can be used to filter this item in action menu.
|
||||
/// </summary>
|
||||
@@ -70,6 +80,12 @@ namespace Content.Shared.Actions
|
||||
/// </summary>
|
||||
public IEnumerable<string> Keywords { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this is an action that requires selecting a target
|
||||
/// </summary>
|
||||
public bool IsTargetAction =>
|
||||
BehaviorType == BehaviorType.TargetEntity || BehaviorType == BehaviorType.TargetPoint;
|
||||
|
||||
public virtual void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||
@@ -106,6 +122,9 @@ namespace Content.Shared.Actions
|
||||
Name, BehaviorType);
|
||||
}
|
||||
|
||||
serializer.DataField(this, x => x.DeselectOnCooldown, "deselectOnCooldown", false);
|
||||
serializer.DataField(this, x => x.DeselectWhenEntityNotClicked, "deselectWhenEntityNotClicked", false);
|
||||
|
||||
serializer.DataReadFunction("filters", new List<string>(),
|
||||
rawTags =>
|
||||
{
|
||||
|
||||
13
Content.Shared/Administration/SetOutfitEuiState.cs
Normal file
13
Content.Shared/Administration/SetOutfitEuiState.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Administration
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class SetOutfitEuiState : EuiStateBase
|
||||
{
|
||||
public EntityUid TargetEntityId;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ namespace Content.Shared.Alert
|
||||
{
|
||||
if (!SupportsSeverity && severity != null)
|
||||
{
|
||||
throw new InvalidOperationException("This alert does not support severity");
|
||||
throw new InvalidOperationException($"This alert ({AlertKey}) does not support severity");
|
||||
}
|
||||
|
||||
if (!SupportsSeverity)
|
||||
@@ -131,24 +131,24 @@ namespace Content.Shared.Alert
|
||||
|
||||
if (severity == null)
|
||||
{
|
||||
throw new ArgumentException("No severity specified but this alert has severity.", nameof(severity));
|
||||
throw new ArgumentException($"No severity specified but this alert ({AlertKey}) has severity.", nameof(severity));
|
||||
}
|
||||
|
||||
if (severity < MinSeverity)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(severity), "Severity below minimum severity.");
|
||||
throw new ArgumentOutOfRangeException(nameof(severity), $"Severity below minimum severity in {AlertKey}.");
|
||||
}
|
||||
|
||||
if (severity > MaxSeverity)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(severity), "Severity above maximum severity.");
|
||||
throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}.");
|
||||
}
|
||||
|
||||
var severityText = severity.Value.ToString(CultureInfo.InvariantCulture);
|
||||
switch (Icon)
|
||||
{
|
||||
case SpriteSpecifier.EntityPrototype entityPrototype:
|
||||
throw new InvalidOperationException("Severity not supported for EntityPrototype icon");
|
||||
throw new InvalidOperationException($"Severity not supported for EntityPrototype icon in {AlertKey}");
|
||||
case SpriteSpecifier.Rsi rsi:
|
||||
return new SpriteSpecifier.Rsi(rsi.RsiPath, rsi.RsiState + severityText);
|
||||
case SpriteSpecifier.Texture texture:
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
Parched,
|
||||
Pulled,
|
||||
Pulling,
|
||||
Magboots,
|
||||
Debug1,
|
||||
Debug2,
|
||||
Debug3,
|
||||
|
||||
@@ -58,9 +58,6 @@ namespace Content.Shared.Arcade
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class BlockGameUserUnregisterMessage : BoundUserInterfaceMessage{}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class BlockGameSetScreenMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.Atmos
|
||||
{
|
||||
@@ -246,6 +248,7 @@ namespace Content.Shared.Atmos
|
||||
/// <summary>
|
||||
/// Gases to Ids. Keep these updated with the prototypes!
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public enum Gas : sbyte
|
||||
{
|
||||
Oxygen = 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Robust.Shared;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Shared
|
||||
@@ -142,6 +142,10 @@ namespace Content.Shared
|
||||
public static readonly CVarDef<string> DatabasePgPassword =
|
||||
CVarDef.Create("database.pg_password", "", CVar.SERVERONLY);
|
||||
|
||||
// Basically only exists for integration tests to avoid race conditions.
|
||||
public static readonly CVarDef<bool> DatabaseSynchronous =
|
||||
CVarDef.Create("database.sync", false, CVar.SERVERONLY);
|
||||
|
||||
|
||||
/*
|
||||
* Outline
|
||||
|
||||
90
Content.Shared/Chemistry/ReactionPrototype.cs
Normal file
90
Content.Shared/Chemistry/ReactionPrototype.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Interfaces.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Prototype for chemical reaction definitions
|
||||
/// </summary>
|
||||
[Prototype("reaction")]
|
||||
public class ReactionPrototype : IPrototype, IIndexedPrototype
|
||||
{
|
||||
private string _id = default!;
|
||||
private string _name = default!;
|
||||
private Dictionary<string, ReactantPrototype> _reactants = default!;
|
||||
private Dictionary<string, ReagentUnit> _products = default!;
|
||||
private List<IReactionEffect> _effects = default!;
|
||||
|
||||
public string ID => _id;
|
||||
public string Name => _name;
|
||||
/// <summary>
|
||||
/// Reactants required for the reaction to occur.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, ReactantPrototype> Reactants => _reactants;
|
||||
/// <summary>
|
||||
/// Reagents created when the reaction occurs.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, ReagentUnit> Products => _products;
|
||||
/// <summary>
|
||||
/// Effects to be triggered when the reaction occurs.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IReactionEffect> Effects => _effects;
|
||||
|
||||
public string? Sound { get; private set; }
|
||||
|
||||
[Dependency] private readonly IModuleManager _moduleManager = default!;
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||
|
||||
serializer.DataField(ref _id, "id", string.Empty);
|
||||
serializer.DataField(ref _name, "name", string.Empty);
|
||||
serializer.DataField(ref _reactants, "reactants", new Dictionary<string, ReactantPrototype>());
|
||||
serializer.DataField(ref _products, "products", new Dictionary<string, ReagentUnit>());
|
||||
serializer.DataField(this, x => x.Sound, "sound", "/Audio/Effects/Chemistry/bubbles.ogg");
|
||||
|
||||
if (_moduleManager.IsServerModule)
|
||||
{
|
||||
//TODO: Don't have a check for if this is the server
|
||||
//Some implementations of IReactionEffect can't currently be moved to shared, so this is here to prevent the client from breaking when reading server-only IReactionEffects.
|
||||
serializer.DataField(ref _effects, "effects", new List<IReactionEffect>());
|
||||
}
|
||||
else
|
||||
{
|
||||
_effects = new(); //To ensure _effects isn't null since it is only serializable on the server right snow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prototype for chemical reaction reactants.
|
||||
/// </summary>
|
||||
public class ReactantPrototype : IExposeData
|
||||
{
|
||||
private ReagentUnit _amount;
|
||||
private bool _catalyst;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum amount of the reactant needed for the reaction to occur.
|
||||
/// </summary>
|
||||
public ReagentUnit Amount => _amount;
|
||||
/// <summary>
|
||||
/// Whether or not the reactant is a catalyst. Catalysts aren't removed when a reaction occurs.
|
||||
/// </summary>
|
||||
public bool Catalyst => _catalyst;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _amount, "amount", ReagentUnit.New(1));
|
||||
serializer.DataField(ref _catalyst, "catalyst", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.Chemistry;
|
||||
@@ -18,16 +19,16 @@ namespace Content.Shared.Chemistry
|
||||
{
|
||||
[Dependency] private readonly IModuleManager _moduleManager = default!;
|
||||
|
||||
private string _id;
|
||||
private string _name;
|
||||
private string _description;
|
||||
private string _physicalDescription;
|
||||
private string _id = default!;
|
||||
private string _name = default!;
|
||||
private string _description = default!;
|
||||
private string _physicalDescription = default!;
|
||||
private Color _substanceColor;
|
||||
private string _spritePath;
|
||||
private List<IMetabolizable> _metabolism;
|
||||
private List<ITileReaction> _tileReactions;
|
||||
private List<IPlantMetabolizable> _plantMetabolism;
|
||||
private float _customPlantMetabolism = 1f;
|
||||
private string _spritePath = default!;
|
||||
private List<IMetabolizable> _metabolism = default!;
|
||||
private List<ITileReaction> _tileReactions = default!;
|
||||
private List<IPlantMetabolizable> _plantMetabolism = default!;
|
||||
private float _customPlantMetabolism;
|
||||
|
||||
public string ID => _id;
|
||||
public string Name => _name;
|
||||
@@ -60,15 +61,17 @@ namespace Content.Shared.Chemistry
|
||||
|
||||
if (_moduleManager.IsServerModule)
|
||||
{
|
||||
//Implementations of the needed interfaces are currently server-only, so they cannot be read on client
|
||||
serializer.DataField(ref _metabolism, "metabolism", new List<IMetabolizable> { new DefaultMetabolizable() });
|
||||
serializer.DataField(ref _tileReactions, "tileReactions", new List<ITileReaction> { });
|
||||
serializer.DataField(ref _plantMetabolism, "plantMetabolism", new List<IPlantMetabolizable> { });
|
||||
}
|
||||
else
|
||||
{
|
||||
//ensure the following fields cannot null since they can only be serialized on server right now
|
||||
_metabolism = new List<IMetabolizable> { new DefaultMetabolizable() };
|
||||
_tileReactions = new List<ITileReaction>(0);
|
||||
_plantMetabolism = new List<IPlantMetabolizable>(0);
|
||||
_tileReactions = new();
|
||||
_plantMetabolism = new();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a quantity of reagent, to a precision of 0.01.
|
||||
/// To enforce this level of precision, floats are shifted by 2 decimal points, rounded, and converted to an int.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ReagentUnit : ISelfSerialize, IComparable<ReagentUnit>, IEquatable<ReagentUnit>
|
||||
{
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -27,6 +31,8 @@ namespace Content.Shared.Chemistry
|
||||
[ViewVariables]
|
||||
public ReagentUnit TotalVolume { get; private set; }
|
||||
|
||||
public Color Color => GetColor();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an empty solution (ex. an empty beaker).
|
||||
/// </summary>
|
||||
@@ -57,6 +63,37 @@ namespace Content.Shared.Chemistry
|
||||
() => _contents);
|
||||
}
|
||||
|
||||
public bool ContainsReagent(string reagentId)
|
||||
{
|
||||
return ContainsReagent(reagentId, out _);
|
||||
}
|
||||
|
||||
public bool ContainsReagent(string reagentId, out ReagentUnit quantity)
|
||||
{
|
||||
foreach (var reagent in Contents)
|
||||
{
|
||||
if (reagent.ReagentId == reagentId)
|
||||
{
|
||||
quantity = reagent.Quantity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
quantity = ReagentUnit.New(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetPrimaryReagentId()
|
||||
{
|
||||
if (Contents.Count == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var majorReagent = Contents.OrderByDescending(reagent => reagent.Quantity).First(); ;
|
||||
return majorReagent.ReagentId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a given quantity of a reagent directly into the solution.
|
||||
/// </summary>
|
||||
@@ -231,6 +268,37 @@ namespace Content.Shared.Chemistry
|
||||
TotalVolume += otherSolution.TotalVolume;
|
||||
}
|
||||
|
||||
private Color GetColor()
|
||||
{
|
||||
if (TotalVolume == 0)
|
||||
{
|
||||
return Color.Transparent;
|
||||
}
|
||||
|
||||
Color mixColor = default;
|
||||
var runningTotalQuantity = ReagentUnit.New(0);
|
||||
|
||||
foreach (var reagent in Contents)
|
||||
{
|
||||
runningTotalQuantity += reagent.Quantity;
|
||||
|
||||
if (!IoCManager.Resolve<IPrototypeManager>().TryIndex(reagent.ReagentId, out ReagentPrototype proto))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mixColor == default)
|
||||
{
|
||||
mixColor = proto.SubstanceColor;
|
||||
continue;
|
||||
}
|
||||
|
||||
var interpolateValue = (1 / runningTotalQuantity.Float()) * reagent.Quantity.Float();
|
||||
mixColor = Color.InterpolateBetween(mixColor, proto.SubstanceColor, interpolateValue);
|
||||
}
|
||||
return mixColor;
|
||||
}
|
||||
|
||||
public Solution Clone()
|
||||
{
|
||||
var volume = ReagentUnit.New(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
@@ -33,6 +33,14 @@ namespace Content.Shared.Chemistry
|
||||
/// <summary>
|
||||
/// Can people examine the solution in the container or is it impossible to see?
|
||||
/// </summary>
|
||||
NoExamine = 8,
|
||||
CanExamine = 8,
|
||||
}
|
||||
|
||||
public static class SolutionContainerCapsHelpers
|
||||
{
|
||||
public static bool HasCap(this SolutionContainerCaps cap, SolutionContainerCaps other)
|
||||
{
|
||||
return (cap & other) == other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Atmos
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum FilterVisuals
|
||||
{
|
||||
VisualState
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class FilterVisualState
|
||||
{
|
||||
public bool Enabled { get; }
|
||||
|
||||
public FilterVisualState(bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Atmos
|
||||
{
|
||||
@@ -14,12 +14,10 @@ namespace Content.Shared.GameObjects.Components.Atmos
|
||||
public class PipeVisualState
|
||||
{
|
||||
public readonly PipeDirection PipeDirection;
|
||||
public readonly ConduitLayer ConduitLayer;
|
||||
|
||||
public PipeVisualState(PipeDirection pipeDirection, ConduitLayer conduitLayer)
|
||||
public PipeVisualState(PipeDirection pipeDirection)
|
||||
{
|
||||
PipeDirection = pipeDirection;
|
||||
ConduitLayer = conduitLayer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +63,15 @@ namespace Content.Shared.GameObjects.Components.Atmos
|
||||
Fourway
|
||||
}
|
||||
|
||||
public enum ConduitLayer
|
||||
{
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Three = 3,
|
||||
}
|
||||
|
||||
public static class PipeDirectionHelpers
|
||||
{
|
||||
public const int PipeDirections = 4;
|
||||
|
||||
public static bool HasDirection(this PipeDirection pipeDirection, PipeDirection other)
|
||||
{
|
||||
return (pipeDirection & other) == other;
|
||||
}
|
||||
|
||||
public static Angle ToAngle(this PipeDirection pipeDirection)
|
||||
{
|
||||
return pipeDirection switch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Atmos
|
||||
@@ -14,16 +14,12 @@ namespace Content.Shared.GameObjects.Components.Atmos
|
||||
{
|
||||
public readonly PipeDirection InletDirection;
|
||||
public readonly PipeDirection OutletDirection;
|
||||
public readonly ConduitLayer InletConduitLayer;
|
||||
public readonly ConduitLayer OutletConduitLayer;
|
||||
public readonly bool PumpEnabled;
|
||||
|
||||
public PumpVisualState(PipeDirection inletDirection, PipeDirection outletDirection, ConduitLayer inletConduitLayer, ConduitLayer outletConduitLayer, bool pumpEnabled)
|
||||
public PumpVisualState(PipeDirection inletDirection, PipeDirection outletDirection, bool pumpEnabled)
|
||||
{
|
||||
InletDirection = inletDirection;
|
||||
OutletDirection = outletDirection;
|
||||
InletConduitLayer = inletConduitLayer;
|
||||
OutletConduitLayer = outletConduitLayer;
|
||||
PumpEnabled = pumpEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,5 +247,10 @@ namespace Content.Shared.GameObjects.Components.Body
|
||||
/// <param name="index">The index to look in.</param>
|
||||
/// <returns>A pair of the part name and body part occupying it.</returns>
|
||||
KeyValuePair<string, IBodyPart> PartAt(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Gibs this body.
|
||||
/// </summary>
|
||||
void Gib(bool gibParts = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,5 +116,10 @@ namespace Content.Shared.GameObjects.Components.Body.Part
|
||||
/// false otherwise.
|
||||
/// </returns>
|
||||
bool DeleteMechanism(IMechanism mechanism);
|
||||
|
||||
/// <summary>
|
||||
/// Gibs the body part.
|
||||
/// </summary>
|
||||
void Gib();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Content.Shared.GameObjects.Components.Body.Surgery;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -313,6 +314,14 @@ namespace Content.Shared.GameObjects.Components.Body.Part
|
||||
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]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.Damage;
|
||||
@@ -11,7 +12,10 @@ 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 Content.Shared.Utility;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Containers;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -19,6 +23,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Component = Robust.Shared.GameObjects.Component;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Body
|
||||
{
|
||||
@@ -697,6 +702,17 @@ namespace Content.Shared.GameObjects.Components.Body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Gib(bool gibParts = false)
|
||||
{
|
||||
foreach (var (_, part) in Parts)
|
||||
{
|
||||
RemovePart(part);
|
||||
|
||||
if (gibParts)
|
||||
part.Gib();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Buckle
|
||||
{
|
||||
@@ -19,6 +20,18 @@ namespace Content.Shared.GameObjects.Components.Buckle
|
||||
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="StrapComponent"/>.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float Range { get; protected set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataReadWriteFunction("range", SharedInteractionSystem.InteractionRange / 1.4f, value => Range = value, () => Range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the entity is buckled, false otherwise.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Shared.GameObjects.Components.Observer;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -28,20 +30,19 @@ namespace Content.Shared.GameObjects.Components.Chemistry.ChemMaster
|
||||
/// <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 IReadOnlyList<Solution.ReagentQuantity> ContainerReagents;
|
||||
/// <summary>
|
||||
/// A list of the reagents and their amounts within the buffer, if applicable.
|
||||
/// </summary>
|
||||
public readonly List<Solution.ReagentQuantity> BufferReagents;
|
||||
public readonly IReadOnlyList<Solution.ReagentQuantity> BufferReagents;
|
||||
public readonly string DispenserName;
|
||||
|
||||
public readonly bool BufferModeTransfer;
|
||||
|
||||
public readonly ReagentUnit BufferCurrentVolume;
|
||||
public readonly ReagentUnit BufferMaxVolume;
|
||||
|
||||
public ChemMasterBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
|
||||
string dispenserName, List<Solution.ReagentQuantity> containerReagents, List<Solution.ReagentQuantity> bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume, ReagentUnit bufferMaxVolume)
|
||||
string dispenserName, IReadOnlyList<Solution.ReagentQuantity> containerReagents, IReadOnlyList<Solution.ReagentQuantity> bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume)
|
||||
{
|
||||
HasPower = hasPower;
|
||||
HasBeaker = hasBeaker;
|
||||
@@ -53,7 +54,6 @@ namespace Content.Shared.GameObjects.Components.Chemistry.ChemMaster
|
||||
BufferReagents = bufferReagents;
|
||||
BufferModeTransfer = bufferModeTransfer;
|
||||
BufferCurrentVolume = bufferCurrentVolume;
|
||||
BufferMaxVolume = bufferMaxVolume;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Content.Shared.GameObjects.Components.Chemistry.ChemMaster
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in <see cref="UiButtonPressedMessage"/> to specify which button was pressed.
|
||||
/// Used in <see cref="AcceptCloningChoiceMessage"/> to specify which button was pressed.
|
||||
/// </summary>
|
||||
public enum UiAction
|
||||
{
|
||||
|
||||
@@ -1,100 +1,229 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
#nullable enable
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Appearance;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
{
|
||||
public abstract class SharedSolutionContainerComponent : Component
|
||||
/// <summary>
|
||||
/// Holds a <see cref="Solution"/> with a limited volume.
|
||||
/// </summary>
|
||||
public abstract class SharedSolutionContainerComponent : Component, IExamine
|
||||
{
|
||||
public override string Name => "SolutionContainer";
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override uint? NetID => ContentNetIDs.SOLUTION;
|
||||
|
||||
private Solution _solution = new();
|
||||
private ReagentUnit _maxVolume;
|
||||
private Color _substanceColor;
|
||||
|
||||
/// <summary>
|
||||
/// The contained solution.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Solution Solution
|
||||
{
|
||||
get => _solution;
|
||||
set
|
||||
{
|
||||
if (_solution == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
public Solution Solution { get; private set; } = new();
|
||||
|
||||
_solution = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
public IReadOnlyList<Solution.ReagentQuantity> ReagentList => Solution.Contents;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ReagentUnit MaxVolume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The total volume of all the of the reagents in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReagentUnit CurrentVolume => Solution.TotalVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum volume of the container.
|
||||
/// Volume needed to fill this container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _maxVolume;
|
||||
set
|
||||
{
|
||||
if (_maxVolume == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
[ViewVariables]
|
||||
public ReagentUnit EmptyVolume => MaxVolume - CurrentVolume;
|
||||
|
||||
_maxVolume = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
[ViewVariables]
|
||||
public virtual Color Color => Solution.Color;
|
||||
|
||||
/// <summary>
|
||||
/// The current blended color of all the reagents in the container.
|
||||
/// If reactions will be checked for when adding reagents to the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual Color SubstanceColor
|
||||
{
|
||||
get => _substanceColor;
|
||||
set
|
||||
{
|
||||
if (_substanceColor == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
public bool CanReact { get; set; }
|
||||
|
||||
_substanceColor = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current capabilities of this container (is the top open to pour? can I inject it into another object?).
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public SolutionContainerCaps Capabilities { get; set; }
|
||||
|
||||
public abstract bool CanAddSolution(Solution solution);
|
||||
public bool CanExamineContents => Capabilities.HasCap(SolutionContainerCaps.CanExamine);
|
||||
|
||||
public abstract bool TryAddSolution(Solution solution, bool skipReactionCheck = false, bool skipColor = false);
|
||||
public bool CanUseWithChemDispenser => Capabilities.HasCap(SolutionContainerCaps.FitsInDispenser);
|
||||
|
||||
public abstract bool TryRemoveReagent(string reagentId, ReagentUnit quantity);
|
||||
public bool CanAddSolutions => Capabilities.HasCap(SolutionContainerCaps.AddTo);
|
||||
|
||||
public bool CanRemoveSolutions => Capabilities.HasCap(SolutionContainerCaps.RemoveFrom);
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, x => x.CanReact, "canReact", true);
|
||||
serializer.DataField(this, x => x.MaxVolume, "maxVol", ReagentUnit.New(0));
|
||||
serializer.DataField(this, x => x.Solution, "contents", new Solution());
|
||||
serializer.DataField(this, x => x.Capabilities, "caps", SolutionContainerCaps.AddTo | SolutionContainerCaps.RemoveFrom | SolutionContainerCaps.CanExamine);
|
||||
}
|
||||
|
||||
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))
|
||||
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)));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new SolutionContainerComponentState(Solution);
|
||||
@@ -102,14 +231,34 @@ namespace Content.Shared.GameObjects.Components.Chemistry
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not SolutionContainerComponentState state)
|
||||
{
|
||||
if (curState is not SolutionContainerComponentState containerState)
|
||||
return;
|
||||
}
|
||||
|
||||
_solution = state.Solution;
|
||||
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;
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Disposal
|
||||
{
|
||||
public abstract class SharedDisposalMailingUnitComponent : SharedDisposalUnitComponent, ICollideSpecial
|
||||
public abstract class SharedDisposalMailingUnitComponent : SharedDisposalUnitComponent
|
||||
{
|
||||
public override string Name => "DisposalMailingUnit";
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
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.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
@@ -12,7 +17,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Disposal
|
||||
{
|
||||
public abstract class SharedDisposalUnitComponent : Component, ICollideSpecial
|
||||
public abstract class SharedDisposalUnitComponent : Component, ICollideSpecial, IDragDropOn
|
||||
{
|
||||
public override string Name => "DisposalUnit";
|
||||
|
||||
@@ -160,5 +165,34 @@ namespace Content.Shared.GameObjects.Components.Disposal
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
public virtual bool CanInsert(IEntity entity)
|
||||
{
|
||||
if (!Anchored)
|
||||
return false;
|
||||
|
||||
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
|
||||
!physics.CanCollide)
|
||||
{
|
||||
if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entity.HasComponent<SharedStorableComponent>() &&
|
||||
!entity.HasComponent<IBody>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return CanInsert(eventArgs.Dragged);
|
||||
}
|
||||
|
||||
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Explosion
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum ClusterFlashVisuals : byte
|
||||
{
|
||||
GrenadesCounter
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
@@ -11,7 +12,7 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Inventory
|
||||
{
|
||||
public abstract class SharedInventoryComponent : Component
|
||||
public abstract class SharedInventoryComponent : Component, IMoveSpeedModifier
|
||||
{
|
||||
// ReSharper disable UnassignedReadonlyField
|
||||
[Dependency] protected readonly IReflectionManager ReflectionManager;
|
||||
@@ -100,5 +101,8 @@ namespace Content.Shared.GameObjects.Components.Inventory
|
||||
Slot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract float WalkSpeedModifier { get; }
|
||||
public abstract float SprintSpeedModifier { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
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.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Medical
|
||||
{
|
||||
public class SharedMedicalScannerComponent : Component
|
||||
public abstract class SharedMedicalScannerComponent : Component, IDragDropOn
|
||||
{
|
||||
public override string Name => "MedicalScanner";
|
||||
|
||||
@@ -78,5 +80,11 @@ namespace Content.Shared.GameObjects.Components.Medical
|
||||
}
|
||||
|
||||
|
||||
public bool CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Dragged.HasComponent<IBody>();
|
||||
}
|
||||
|
||||
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
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};
|
||||
|
||||
|
||||
@@ -232,6 +232,8 @@ namespace Content.Shared.GameObjects.Components.Mobs
|
||||
|
||||
KnockdownTimer -= _helpKnockdownRemove;
|
||||
|
||||
OnInteractHand();
|
||||
|
||||
SetAlert();
|
||||
Dirty();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -54,6 +56,15 @@ namespace Content.Shared.GameObjects.Components.Movement
|
||||
_movespeedModifiersNeedRefresh = true;
|
||||
}
|
||||
|
||||
public static void RefreshItemModifiers(IEntity item)
|
||||
{
|
||||
if (item.TryGetContainer(out var container) &&
|
||||
container.Owner.TryGetComponent(out MovementSpeedModifierComponent mod))
|
||||
{
|
||||
mod.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
@@ -1,11 +1,34 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Movement
|
||||
{
|
||||
public interface IClimbable { };
|
||||
|
||||
public class SharedClimbableComponent : Component, IClimbable
|
||||
public abstract class SharedClimbableComponent : Component, IClimbable, IDragDropOn
|
||||
{
|
||||
public sealed override string Name => "Climbable";
|
||||
|
||||
/// <summary>
|
||||
/// The range from which this entity can be climbed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
protected float Range;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref Range, "range", SharedInteractionSystem.InteractionRange / 1.4f);
|
||||
}
|
||||
|
||||
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Dragged.HasComponent<SharedClimbingComponent>();
|
||||
}
|
||||
|
||||
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Movement
|
||||
{
|
||||
public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial, IDraggable
|
||||
public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial
|
||||
{
|
||||
public sealed override string Name => "Climbing";
|
||||
public sealed override uint? NetID => ContentNetIDs.CLIMBING;
|
||||
@@ -47,16 +46,6 @@ namespace Content.Shared.GameObjects.Components.Movement
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IDraggable.CanDrop(CanDropEventArgs args)
|
||||
{
|
||||
return args.Target.HasComponent<IClimbable>();
|
||||
}
|
||||
|
||||
bool IDraggable.Drop(DragDropEventArgs args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
@@ -11,6 +12,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
|
||||
public sealed override uint? NetID => ContentNetIDs.HUNGER;
|
||||
|
||||
[ViewVariables]
|
||||
public abstract HungerThreshold CurrentHungerThreshold { get; }
|
||||
|
||||
|
||||
@@ -20,7 +22,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
if (CurrentHungerThreshold == HungerThreshold.Starving)
|
||||
{
|
||||
return 0.5f;
|
||||
return 0.75f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -31,7 +33,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
if (CurrentHungerThreshold == HungerThreshold.Starving)
|
||||
{
|
||||
return 0.5f;
|
||||
return 0.75f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -49,6 +51,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum HungerThreshold : byte
|
||||
{
|
||||
Overfed,
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
@@ -11,6 +12,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
|
||||
public sealed override uint? NetID => ContentNetIDs.THIRST;
|
||||
|
||||
[ViewVariables]
|
||||
public abstract ThirstThreshold CurrentThirstThreshold { get; }
|
||||
|
||||
float IMoveSpeedModifier.SprintSpeedModifier
|
||||
@@ -19,7 +21,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
if (CurrentThirstThreshold == ThirstThreshold.Parched)
|
||||
{
|
||||
return 0.25f;
|
||||
return 0.75f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -30,7 +32,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
{
|
||||
if (CurrentThirstThreshold == ThirstThreshold.Parched)
|
||||
{
|
||||
return 0.5f;
|
||||
return 0.75f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -49,6 +51,7 @@ namespace Content.Shared.GameObjects.Components.Nutrition
|
||||
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum ThirstThreshold : byte
|
||||
{
|
||||
// Hydrohomies
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Observer
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum AcceptCloningUiButton
|
||||
{
|
||||
Deny,
|
||||
Accept,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class AcceptCloningChoiceMessage : EuiMessageBase
|
||||
{
|
||||
public readonly AcceptCloningUiButton Button;
|
||||
|
||||
public AcceptCloningChoiceMessage(AcceptCloningUiButton button)
|
||||
{
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
public class SharedAcceptCloningComponent : Component
|
||||
{
|
||||
public override string Name => "AcceptCloning";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum AcceptCloningUiKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum UiButton
|
||||
{
|
||||
Accept
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class UiButtonPressedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly UiButton Button;
|
||||
|
||||
public UiButtonPressedMessage(UiButton button)
|
||||
{
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
13
Content.Shared/GameObjects/Components/SharedBurningStates.cs
Normal file
13
Content.Shared/GameObjects/Components/SharedBurningStates.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum SharedBurningStates : byte
|
||||
{
|
||||
Unlit,
|
||||
Lit,
|
||||
Burnt,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
public abstract class SharedMagbootsComponent : Component, IMoveSpeedModifier
|
||||
{
|
||||
public sealed override string Name => "Magboots";
|
||||
public sealed override uint? NetID => ContentNetIDs.MAGBOOTS;
|
||||
|
||||
public abstract bool On { get; set; }
|
||||
|
||||
|
||||
protected void OnChanged()
|
||||
{
|
||||
MovementSpeedModifierComponent.RefreshItemModifiers(Owner);
|
||||
}
|
||||
|
||||
public float WalkSpeedModifier => On ? 0.85f : 1;
|
||||
public float SprintSpeedModifier => On ? 0.65f : 1;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MagbootsComponentState : ComponentState
|
||||
{
|
||||
public bool On { get; }
|
||||
|
||||
public MagbootsComponentState(bool @on) : base(ContentNetIDs.MAGBOOTS)
|
||||
{
|
||||
On = on;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
@@ -8,18 +9,23 @@ namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
public override string Name => "PlaceableSurface";
|
||||
public override uint? NetID => ContentNetIDs.PLACEABLE_SURFACE;
|
||||
|
||||
public virtual bool IsPlaceable { get; set; }
|
||||
public virtual bool PlaceCentered { get; set; }
|
||||
public virtual Vector2 PositionOffset { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class PlaceableSurfaceComponentState : ComponentState
|
||||
{
|
||||
public readonly bool IsPlaceable;
|
||||
public readonly bool PlaceCentered;
|
||||
public readonly Vector2 PositionOffset;
|
||||
|
||||
public PlaceableSurfaceComponentState(bool placeable) : base(ContentNetIDs.PLACEABLE_SURFACE)
|
||||
public PlaceableSurfaceComponentState(bool placeable, bool centered, Vector2 offset) : base(ContentNetIDs.PLACEABLE_SURFACE)
|
||||
{
|
||||
IsPlaceable = placeable;
|
||||
PlaceCentered = centered;
|
||||
PositionOffset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@ namespace Content.Shared.GameObjects.Components
|
||||
_count = value;
|
||||
if (_count <= 0)
|
||||
{
|
||||
if (Owner.TryGetContainerMan(out var containerManager))
|
||||
{
|
||||
containerManager.Remove(Owner);
|
||||
}
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
|
||||
11
Content.Shared/GameObjects/Components/SmokingVisuals.cs
Normal file
11
Content.Shared/GameObjects/Components/SmokingVisuals.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum SmokingVisuals : byte
|
||||
{
|
||||
Smoking,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Buckle;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -23,11 +27,21 @@ namespace Content.Shared.GameObjects.Components.Strap
|
||||
Down
|
||||
}
|
||||
|
||||
public abstract class SharedStrapComponent : Component
|
||||
public abstract class SharedStrapComponent : Component, IDragDropOn
|
||||
{
|
||||
public sealed override string Name => "Strap";
|
||||
|
||||
public sealed override uint? NetID => ContentNetIDs.STRAP;
|
||||
|
||||
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Dragged.TryGetComponent(out SharedBuckleComponent buckleComponent)) return false;
|
||||
bool Ignored(IEntity entity) => entity == eventArgs.User || entity == eventArgs.Dragged || entity == eventArgs.Target;
|
||||
|
||||
return eventArgs.Target.InRangeUnobstructed(eventArgs.Dragged, buckleComponent.Range, predicate: Ignored);
|
||||
}
|
||||
|
||||
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -18,58 +18,13 @@ namespace Content.Shared.GameObjects.Components.Suspicion
|
||||
{
|
||||
public readonly string? Role;
|
||||
public readonly bool? Antagonist;
|
||||
public readonly (string name, EntityUid)[] Allies;
|
||||
|
||||
public SuspicionRoleComponentState(string? role, bool? antagonist) : base(ContentNetIDs.SUSPICION_ROLE)
|
||||
public SuspicionRoleComponentState(string? role, bool? antagonist, (string name, EntityUid)[] allies) : base(ContentNetIDs.SUSPICION_ROLE)
|
||||
{
|
||||
Role = role;
|
||||
Antagonist = antagonist;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SuspicionAlliesMessage : ComponentMessage
|
||||
{
|
||||
public readonly HashSet<EntityUid> Allies;
|
||||
|
||||
public SuspicionAlliesMessage(HashSet<EntityUid> allies)
|
||||
{
|
||||
Directed = true;
|
||||
Allies = allies;
|
||||
}
|
||||
|
||||
public SuspicionAlliesMessage(IEnumerable<EntityUid> allies) : this(allies.ToHashSet()) { }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SuspicionAllyAddedMessage : ComponentMessage
|
||||
{
|
||||
public readonly EntityUid Ally;
|
||||
|
||||
public SuspicionAllyAddedMessage(EntityUid ally)
|
||||
{
|
||||
Directed = true;
|
||||
Ally = ally;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SuspicionAllyRemovedMessage : ComponentMessage
|
||||
{
|
||||
public readonly EntityUid Ally;
|
||||
|
||||
public SuspicionAllyRemovedMessage(EntityUid ally)
|
||||
{
|
||||
Directed = true;
|
||||
Ally = ally;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SuspicionAlliesClearedMessage : ComponentMessage
|
||||
{
|
||||
public SuspicionAlliesClearedMessage()
|
||||
{
|
||||
Directed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Utensil
|
||||
{
|
||||
[Flags]
|
||||
public enum UtensilType : byte
|
||||
{
|
||||
None = 0,
|
||||
Fork = 1,
|
||||
Spoon = 1 << 1,
|
||||
Knife = 1 << 2
|
||||
}
|
||||
|
||||
public class SharedUtensilComponent : Component
|
||||
{
|
||||
public override string Name => "Utensil";
|
||||
|
||||
public virtual UtensilType Types { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#nullable enable
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Watercloset
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum ToiletVisuals
|
||||
{
|
||||
LidOpen,
|
||||
SeatUp
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@
|
||||
public const uint REAGENT_GRINDER = 1082;
|
||||
public const uint ACTIONS = 1083;
|
||||
public const uint DAMAGEABLE = 1084;
|
||||
public const uint MAGBOOTS = 1085;
|
||||
|
||||
// Net IDs for integration tests.
|
||||
public const uint PREDICTION_TEST = 10001;
|
||||
|
||||
@@ -30,5 +30,18 @@ namespace Content.Shared.GameObjects.EntitySystemMessages
|
||||
public bool TextureEffect { get; }
|
||||
public bool ArcFollowAttacker { get; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlayLungeAnimationMessage : EntitySystemMessage
|
||||
{
|
||||
public Angle Angle { get; }
|
||||
public EntityUid Source { get; }
|
||||
|
||||
public PlayLungeAnimationMessage(Angle angle, EntityUid source)
|
||||
{
|
||||
Angle = angle;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
Content.Shared/GameObjects/EntitySystems/ChemistrySystem.cs
Normal file
45
Content.Shared/GameObjects/EntitySystems/ChemistrySystem.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed
|
||||
/// </summary>
|
||||
public interface ISolutionChange
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when solution is mixed with some other solution, or when some part of the solution is removed
|
||||
/// </summary>
|
||||
void SolutionChanged(SolutionChangeEventArgs eventArgs);
|
||||
}
|
||||
|
||||
public class SolutionChangeEventArgs : EventArgs
|
||||
{
|
||||
public IEntity Owner { get; set; }
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ChemistrySystem : EntitySystem
|
||||
{
|
||||
public void HandleSolutionChange(IEntity owner)
|
||||
{
|
||||
var eventArgs = new SolutionChangeEventArgs
|
||||
{
|
||||
Owner = owner,
|
||||
};
|
||||
var solutionChangeArgs = owner.GetAllComponents<ISolutionChange>().ToList();
|
||||
|
||||
foreach (var solutionChangeArg in solutionChangeArgs)
|
||||
{
|
||||
solutionChangeArg.SolutionChanged(eventArgs);
|
||||
|
||||
if (owner.Deleted)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ using Content.Shared.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
@@ -161,7 +160,7 @@ namespace Content.Shared.GameObjects.EntitySystems
|
||||
public static bool InRangeUnOccluded(AfterInteractEventArgs args, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
|
||||
{
|
||||
var originPos = args.User.Transform.MapPosition;
|
||||
var otherPos = args.Target.Transform.MapPosition;
|
||||
var otherPos = args.Target?.Transform.MapPosition ?? args.ClickLocation.ToMap(args.User.EntityManager);
|
||||
|
||||
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Shared.GameObjects.EntitySystems
|
||||
{
|
||||
public abstract class SharedChemicalReactionSystem : EntitySystem
|
||||
{
|
||||
private IEnumerable<ReactionPrototype> _reactions;
|
||||
|
||||
private const int MaxReactionIterations = 20;
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a solution can undergo a specified reaction.
|
||||
/// </summary>
|
||||
/// <param name="solution">The solution to check.</param>
|
||||
/// <param name="reaction">The reaction to check.</param>
|
||||
/// <param name="lowestUnitReactions">How many times this reaction can occur.</param>
|
||||
/// <returns></returns>
|
||||
private static bool CanReact(Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions)
|
||||
{
|
||||
lowestUnitReactions = ReagentUnit.MaxValue;
|
||||
|
||||
foreach (var reactantData in reaction.Reactants)
|
||||
{
|
||||
var reactantName = reactantData.Key;
|
||||
var reactantCoefficient = reactantData.Value.Amount;
|
||||
|
||||
if (!solution.ContainsReagent(reactantName, out var reactantQuantity))
|
||||
return false;
|
||||
|
||||
var unitReactions = reactantQuantity / reactantCoefficient;
|
||||
|
||||
if (unitReactions < lowestUnitReactions)
|
||||
{
|
||||
lowestUnitReactions = unitReactions;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a reaction on a solution. This assumes all reaction criteria are met.
|
||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||
/// </summary>
|
||||
private Solution PerformReaction(Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions)
|
||||
{
|
||||
//Remove reactants
|
||||
foreach (var reactant in reaction.Reactants)
|
||||
{
|
||||
if (!reactant.Value.Catalyst)
|
||||
{
|
||||
var amountToRemove = unitReactions * reactant.Value.Amount;
|
||||
solution.RemoveReagent(reactant.Key, amountToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
//Create products
|
||||
var products = new Solution();
|
||||
foreach (var product in reaction.Products)
|
||||
{
|
||||
products.AddReagent(product.Key, product.Value * unitReactions);
|
||||
}
|
||||
|
||||
// Trigger reaction effects
|
||||
OnReaction(reaction, owner, unitReactions);
|
||||
|
||||
return products;
|
||||
}
|
||||
|
||||
protected virtual void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
|
||||
{
|
||||
foreach (var effect in reaction.Effects)
|
||||
{
|
||||
effect.React(owner, unitReactions.Double());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs all chemical reactions that can be run on a solution.
|
||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||
/// WARNING: Does not trigger reactions between solution and new products.
|
||||
/// </summary>
|
||||
private Solution ProcessReactions(Solution solution, IEntity owner)
|
||||
{
|
||||
//TODO: make a hashmap at startup and then look up reagents in the contents for a reaction
|
||||
var overallProducts = new Solution();
|
||||
foreach (var reaction in _reactions)
|
||||
{
|
||||
if (CanReact(solution, reaction, out var unitReactions))
|
||||
{
|
||||
var reactionProducts = PerformReaction(solution, owner, reaction, unitReactions);
|
||||
overallProducts.AddSolution(reactionProducts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return overallProducts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continually react a solution until no more reactions occur.
|
||||
/// </summary>
|
||||
public void FullyReactSolution(Solution solution, IEntity owner)
|
||||
{
|
||||
for (var i = 0; i < MaxReactionIterations; i++)
|
||||
{
|
||||
var products = ProcessReactions(solution, owner);
|
||||
|
||||
if (products.TotalVolume <= 0)
|
||||
return;
|
||||
|
||||
solution.AddSolution(products);
|
||||
}
|
||||
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continually react a solution until no more reactions occur, with a volume constraint.
|
||||
/// If a reaction's products would exceed the max volume, some product is deleted.
|
||||
/// </summary>
|
||||
public void FullyReactSolution(Solution solution, IEntity owner, ReagentUnit maxVolume)
|
||||
{
|
||||
for (var i = 0; i < MaxReactionIterations; i++)
|
||||
{
|
||||
var products = ProcessReactions(solution, owner);
|
||||
|
||||
if (products.TotalVolume <= 0)
|
||||
return;
|
||||
|
||||
var totalVolume = solution.TotalVolume + products.TotalVolume;
|
||||
var excessVolume = totalVolume - maxVolume;
|
||||
|
||||
if (excessVolume > 0)
|
||||
{
|
||||
products.RemoveSolution(excessVolume); //excess product is deleted to fit under volume limit
|
||||
}
|
||||
|
||||
solution.AddSolution(products);
|
||||
}
|
||||
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace Content.Shared.GameTicking
|
||||
public bool IsRoundStarted { get; set; }
|
||||
public bool YouAreReady { get; set; }
|
||||
// UTC.
|
||||
public DateTime StartTime { get; set; }
|
||||
public TimeSpan StartTime { get; set; }
|
||||
public bool Paused { get; set; }
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
@@ -105,7 +105,7 @@ namespace Content.Shared.GameTicking
|
||||
}
|
||||
|
||||
YouAreReady = buffer.ReadBoolean();
|
||||
StartTime = new DateTime(buffer.ReadInt64(), DateTimeKind.Utc);
|
||||
StartTime = new TimeSpan(buffer.ReadInt64());
|
||||
Paused = buffer.ReadBoolean();
|
||||
}
|
||||
|
||||
@@ -158,9 +158,9 @@ namespace Content.Shared.GameTicking
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The total amount of seconds to go until the countdown finishes
|
||||
/// The game time that the game will start at.
|
||||
/// </summary>
|
||||
public DateTime StartTime { get; set; }
|
||||
public TimeSpan StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the countdown is paused
|
||||
@@ -169,7 +169,7 @@ namespace Content.Shared.GameTicking
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
StartTime = new DateTime(buffer.ReadInt64(), DateTimeKind.Utc);
|
||||
StartTime = new TimeSpan(buffer.ReadInt64());
|
||||
Paused = buffer.ReadBoolean();
|
||||
}
|
||||
|
||||
|
||||
13
Content.Shared/Interfaces/Chemistry/IReactionEffect.cs
Normal file
13
Content.Shared/Interfaces/Chemistry/IReactionEffect.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
|
||||
namespace Content.Server.Interfaces.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Chemical reaction effect on the world such as an explosion, EMP, or fire.
|
||||
/// </summary>
|
||||
public interface IReactionEffect : IExposeData
|
||||
{
|
||||
void React(IEntity solutionEntity, double intensity);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,10 +24,18 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
|
||||
public class AfterInteractEventArgs : EventArgs
|
||||
{
|
||||
public IEntity User { get; set; }
|
||||
public EntityCoordinates ClickLocation { get; set; }
|
||||
public IEntity Target { get; set; }
|
||||
public bool CanReach { get; set; }
|
||||
public IEntity User { get; }
|
||||
public EntityCoordinates ClickLocation { get; }
|
||||
public IEntity? Target { get; }
|
||||
public bool CanReach { get; }
|
||||
|
||||
public AfterInteractEventArgs(IEntity user, EntityCoordinates clickLocation, IEntity? target, bool canReach)
|
||||
{
|
||||
User = user;
|
||||
ClickLocation = clickLocation;
|
||||
Target = target;
|
||||
CanReach = canReach;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +62,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
/// <summary>
|
||||
/// Entity that was attacked. This can be null if the attack did not click on an entity.
|
||||
/// </summary>
|
||||
public IEntity Attacked { get; }
|
||||
public IEntity? Attacked { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Location that the user clicked outside of their interaction range.
|
||||
@@ -65,7 +75,8 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
/// </summary>
|
||||
public bool CanReach { get; }
|
||||
|
||||
public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity attacked, EntityCoordinates clickLocation, bool canReach)
|
||||
public AfterInteractMessage(IEntity user, IEntity itemInHand, IEntity? attacked,
|
||||
EntityCoordinates clickLocation, bool canReach)
|
||||
{
|
||||
User = user;
|
||||
Attacked = attacked;
|
||||
@@ -74,5 +85,4 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
CanReach = canReach;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
public interface IDragDropOn
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked server-side when another entity is being dragged and dropped
|
||||
/// Invoked when another entity is being dragged and dropped
|
||||
/// onto this one before invoking <see cref="DragDropOn"/>.
|
||||
/// Note that other drag and drop interactions may be attempted if
|
||||
/// this one fails.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components hot quality when they are used.
|
||||
/// E.g if you hold a lit match or a welder then it will be hot,
|
||||
/// presuming match is lit or the welder is on respectively.
|
||||
/// However say you hold an item that is always hot like lava rock,
|
||||
/// it will be permanently hot.
|
||||
/// </summary>
|
||||
public interface IHotItem
|
||||
{
|
||||
bool IsCurrentlyHot();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -23,6 +23,7 @@ namespace Content.Shared.Physics
|
||||
GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields
|
||||
Underplating = 1 << 7, // 128 Things that are under plating
|
||||
Passable = 1 << 8, // 256 Things that are passable
|
||||
ExplosivePassable = 1 << 9, // 512 Things that let the pressure of a explosion through
|
||||
MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid.
|
||||
|
||||
MobMask = Impassable | MobImpassable | VaultImpassable | SmallImpassable,
|
||||
|
||||
12
Content.Shared/Preferences/BackpackPreference.cs
Normal file
12
Content.Shared/Preferences/BackpackPreference.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Content.Shared.Preferences
|
||||
{
|
||||
/// <summary>
|
||||
/// The backpack preference for a profile. Stored in database!
|
||||
/// </summary>
|
||||
public enum BackpackPreference
|
||||
{
|
||||
Backpack,
|
||||
Satchel,
|
||||
Duffelbag
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -34,6 +34,7 @@ namespace Content.Shared.Preferences
|
||||
Gender gender,
|
||||
HumanoidCharacterAppearance appearance,
|
||||
ClothingPreference clothing,
|
||||
BackpackPreference backpack,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
List<string> antagPreferences)
|
||||
@@ -44,6 +45,7 @@ namespace Content.Shared.Preferences
|
||||
Gender = gender;
|
||||
Appearance = appearance;
|
||||
Clothing = clothing;
|
||||
Backpack = backpack;
|
||||
_jobPriorities = jobPriorities;
|
||||
PreferenceUnavailable = preferenceUnavailable;
|
||||
_antagPreferences = antagPreferences;
|
||||
@@ -54,7 +56,7 @@ namespace Content.Shared.Preferences
|
||||
HumanoidCharacterProfile other,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
List<string> antagPreferences)
|
||||
: this(other.Name, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing,
|
||||
: this(other.Name, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack,
|
||||
jobPriorities, other.PreferenceUnavailable, antagPreferences)
|
||||
{
|
||||
}
|
||||
@@ -72,10 +74,11 @@ namespace Content.Shared.Preferences
|
||||
Gender gender,
|
||||
HumanoidCharacterAppearance appearance,
|
||||
ClothingPreference clothing,
|
||||
BackpackPreference backpack,
|
||||
IReadOnlyDictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
IReadOnlyList<string> antagPreferences)
|
||||
: this(name, age, sex, gender, appearance, clothing, new Dictionary<string, JobPriority>(jobPriorities),
|
||||
: this(name, age, sex, gender, appearance, clothing, backpack, new Dictionary<string, JobPriority>(jobPriorities),
|
||||
preferenceUnavailable, new List<string>(antagPreferences))
|
||||
{
|
||||
}
|
||||
@@ -98,7 +101,7 @@ namespace Content.Shared.Preferences
|
||||
var name = $"{firstName} {lastName}";
|
||||
var age = random.Next(MinimumAge, MaximumAge);
|
||||
|
||||
return new HumanoidCharacterProfile(name, age, sex, gender, HumanoidCharacterAppearance.Random(sex), ClothingPreference.Jumpsuit,
|
||||
return new HumanoidCharacterProfile(name, age, sex, gender, HumanoidCharacterAppearance.Random(sex), ClothingPreference.Jumpsuit, BackpackPreference.Backpack,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
{SharedGameTicker.OverflowJob, JobPriority.High}
|
||||
@@ -112,6 +115,7 @@ namespace Content.Shared.Preferences
|
||||
public ICharacterAppearance CharacterAppearance => Appearance;
|
||||
public HumanoidCharacterAppearance Appearance { get; private set; }
|
||||
public ClothingPreference Clothing { get; private set; }
|
||||
public BackpackPreference Backpack { get; private set; }
|
||||
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
|
||||
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
|
||||
public PreferenceUnavailableMode PreferenceUnavailable { get; private set; }
|
||||
@@ -145,7 +149,10 @@ namespace Content.Shared.Preferences
|
||||
{
|
||||
return new(this) { Clothing = clothing };
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithBackpackPreference(BackpackPreference backpack)
|
||||
{
|
||||
return new(this) { Backpack = backpack };
|
||||
}
|
||||
public HumanoidCharacterProfile WithJobPriorities(IEnumerable<KeyValuePair<string, JobPriority>> jobPriorities)
|
||||
{
|
||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences);
|
||||
@@ -221,7 +228,7 @@ namespace Content.Shared.Preferences
|
||||
string name;
|
||||
if (string.IsNullOrEmpty(profile.Name))
|
||||
{
|
||||
name = "John Doe";
|
||||
name = "Urist McHands";
|
||||
}
|
||||
else if (profile.Name.Length > MaxNameLength)
|
||||
{
|
||||
@@ -254,6 +261,14 @@ namespace Content.Shared.Preferences
|
||||
_ => ClothingPreference.Jumpsuit // Invalid enum values.
|
||||
};
|
||||
|
||||
var backpack = profile.Backpack switch
|
||||
{
|
||||
BackpackPreference.Backpack => BackpackPreference.Backpack,
|
||||
BackpackPreference.Satchel => BackpackPreference.Satchel,
|
||||
BackpackPreference.Duffelbag => BackpackPreference.Duffelbag,
|
||||
_ => BackpackPreference.Backpack // Invalid enum values.
|
||||
};
|
||||
|
||||
var priorities = new Dictionary<string, JobPriority>(profile.JobPriorities
|
||||
.Where(p => prototypeManager.HasIndex<JobPrototype>(p.Key) && p.Value switch
|
||||
{
|
||||
@@ -268,11 +283,11 @@ namespace Content.Shared.Preferences
|
||||
.Where(prototypeManager.HasIndex<AntagPrototype>)
|
||||
.ToList();
|
||||
|
||||
return new HumanoidCharacterProfile(name, age, sex, gender, appearance, clothing, priorities, prefsUnavailableMode, antags);
|
||||
return new HumanoidCharacterProfile(name, age, sex, gender, appearance, clothing, backpack, priorities, prefsUnavailableMode, antags);
|
||||
}
|
||||
|
||||
public string Summary =>
|
||||
Loc.GetString("{0}, {1} years old human. {2:Their} pronouns are {2:they}/{2:them}.", Name, Age, this);
|
||||
Loc.GetString(" This is {0}. {2:They} {2:are} {1} years old.", Name, Age, this);
|
||||
|
||||
public bool MemberwiseEquals(ICharacterProfile maybeOther)
|
||||
{
|
||||
@@ -283,6 +298,7 @@ namespace Content.Shared.Preferences
|
||||
if (Gender != other.Gender) return false;
|
||||
if (PreferenceUnavailable != other.PreferenceUnavailable) return false;
|
||||
if (Clothing != other.Clothing) return false;
|
||||
if (Backpack != other.Backpack) return false;
|
||||
if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false;
|
||||
if (!_antagPreferences.SequenceEqual(other._antagPreferences)) return false;
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
@@ -302,7 +318,8 @@ namespace Content.Shared.Preferences
|
||||
Sex,
|
||||
Gender,
|
||||
Appearance,
|
||||
Clothing
|
||||
Clothing,
|
||||
Backpack
|
||||
),
|
||||
PreferenceUnavailable,
|
||||
_jobPriorities,
|
||||
|
||||
25
Content.Shared/Prototypes/DatasetPrototype.cs
Normal file
25
Content.Shared/Prototypes/DatasetPrototype.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Prototypes
|
||||
{
|
||||
[Prototype("dataset")]
|
||||
public class DatasetPrototype : IPrototype, IIndexedPrototype
|
||||
{
|
||||
private string _id;
|
||||
public string ID => _id;
|
||||
|
||||
private List<string> _values;
|
||||
public IReadOnlyList<string> Values => _values;
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
var ser = YamlObjectSerializer.NewReader(mapping);
|
||||
|
||||
ser.DataField(ref _id, "id", "");
|
||||
ser.DataField(ref _values, "values", new List<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ namespace Content.Shared.Roles
|
||||
/// if empty, there is no skirt override - instead the uniform provided in equipment is added.
|
||||
/// </summary>
|
||||
private string _innerClothingSkirt = default!;
|
||||
private string _satchel = default!;
|
||||
private string _duffelbag = default!;
|
||||
|
||||
public IReadOnlyDictionary<string, string> Inhand => _inHand;
|
||||
/// <summary>
|
||||
@@ -51,6 +53,8 @@ namespace Content.Shared.Roles
|
||||
}, type => type.Value);
|
||||
|
||||
serializer.DataField(ref _innerClothingSkirt, "innerclothingskirt", string.Empty);
|
||||
serializer.DataField(ref _satchel, "satchel", string.Empty);
|
||||
serializer.DataField(ref _duffelbag, "duffelbag", string.Empty);
|
||||
}
|
||||
|
||||
public string GetGear(Slots slot, HumanoidCharacterProfile? profile)
|
||||
@@ -59,6 +63,10 @@ namespace Content.Shared.Roles
|
||||
{
|
||||
if ((slot == Slots.INNERCLOTHING) && (profile.Clothing == ClothingPreference.Jumpskirt) && (_innerClothingSkirt != ""))
|
||||
return _innerClothingSkirt;
|
||||
if ((slot == Slots.BACKPACK) && (profile.Backpack == BackpackPreference.Satchel) && (_satchel != ""))
|
||||
return _satchel;
|
||||
if ((slot == Slots.BACKPACK) && (profile.Backpack == BackpackPreference.Duffelbag) && (_duffelbag != ""))
|
||||
return _duffelbag;
|
||||
}
|
||||
|
||||
if (_equipment.ContainsKey(slot))
|
||||
|
||||
Reference in New Issue
Block a user