Merge remote-tracking branch 'upstream/master' into upstream
# Conflicts: # Content.Server/Body/Components/BloodstreamComponent.cs # Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs # Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml # Resources/Prototypes/Entities/Structures/Machines/lathe.yml # Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_chem.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_gene.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_robo.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/labcoat_viro.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Coats/rndcoat.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Coats/rndcoat.rsi/open-inhand-right.png # Resources/Textures/Clothing/OuterClothing/Hardsuits/evaprisoner.rsi/open-inhand-left.png # Resources/Textures/Clothing/OuterClothing/Hardsuits/evaprisoner.rsi/open-inhand-right.png
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Atmos
|
||||
@@ -15,6 +16,8 @@ namespace Content.Shared.Atmos
|
||||
South = 1 << 1, // 2
|
||||
East = 1 << 2, // 4
|
||||
West = 1 << 3, // 8
|
||||
// If more directions are added, note that AtmosDirectionHelpers.ToOppositeIndex() expects opposite directions
|
||||
// to come in pairs
|
||||
|
||||
NorthEast = North | East, // 5
|
||||
SouthEast = South | East, // 6
|
||||
@@ -42,6 +45,22 @@ namespace Content.Shared.Atmos
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This returns the index that corresponds to the opposite direction of some other direction index.
|
||||
/// I.e., <c>1<<OppositeIndex(i) == (1<<i).GetOpposite()</c>
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToOppositeIndex(this int index)
|
||||
{
|
||||
return index ^ 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AtmosDirection ToOppositeDir(this int index)
|
||||
{
|
||||
return (AtmosDirection) (1 << (index ^ 1));
|
||||
}
|
||||
|
||||
public static Direction ToDirection(this AtmosDirection direction)
|
||||
{
|
||||
return direction switch
|
||||
@@ -119,10 +138,11 @@ namespace Content.Shared.Atmos
|
||||
return angle.GetDir().ToAtmosDirection();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToIndex(this AtmosDirection direction)
|
||||
{
|
||||
// This will throw if you pass an invalid direction. Not this method's fault, but yours!
|
||||
return (int) Math.Log2((int) direction);
|
||||
return BitOperations.Log2((uint)direction);
|
||||
}
|
||||
|
||||
public static AtmosDirection WithFlag(this AtmosDirection direction, AtmosDirection other)
|
||||
|
||||
33
Content.Shared/Chemistry/Components/HyposprayComponent.cs
Normal file
33
Content.Shared/Chemistry/Components/HyposprayComponent.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class HyposprayComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public string SolutionName = "hypospray";
|
||||
|
||||
// TODO: This should be on clumsycomponent.
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ClumsyFailChance = 0.5f;
|
||||
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 TransferAmount = FixedPoint2.New(5);
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Decides whether you can inject everything or just mobs.
|
||||
/// When you can only affect mobs, you're capable of drawing from beakers.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
[DataField(required: true)]
|
||||
public bool OnlyAffectsMobs = false;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
[NetworkedComponent()]
|
||||
public abstract partial class SharedHyposprayComponent : Component
|
||||
{
|
||||
[DataField("solutionName")]
|
||||
public string SolutionName = "hypospray";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class HyposprayComponentState : ComponentState
|
||||
{
|
||||
public FixedPoint2 CurVolume { get; }
|
||||
public FixedPoint2 MaxVolume { get; }
|
||||
|
||||
public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume)
|
||||
{
|
||||
CurVolume = curVolume;
|
||||
MaxVolume = maxVolume;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Administration.Logs;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
public abstract class SharedHypospraySystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] protected readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] protected readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||
[Dependency] protected readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] protected readonly ReactiveSystem _reactiveSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<HyposprayComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleModeVerb);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// Uses the OnlyMobs field as a check to implement the ability
|
||||
// to draw from jugs and containers with the hypospray
|
||||
// Toggleable to allow people to inject containers if they prefer it over drawing
|
||||
// </summary>
|
||||
private void AddToggleModeVerb(Entity<HyposprayComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
||||
var (_, component) = entity;
|
||||
var user = args.User;
|
||||
var verb = new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString("hypospray-verb-mode-label"),
|
||||
Act = () =>
|
||||
{
|
||||
ToggleMode(entity, user);
|
||||
}
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void ToggleMode(Entity<HyposprayComponent> entity, EntityUid user)
|
||||
{
|
||||
SetMode(entity, !entity.Comp.OnlyAffectsMobs);
|
||||
string msg = entity.Comp.OnlyAffectsMobs ? "hypospray-verb-mode-inject-mobs-only" : "hypospray-verb-mode-inject-all";
|
||||
_popup.PopupClient(Loc.GetString(msg), entity, user);
|
||||
}
|
||||
|
||||
public void SetMode(Entity<HyposprayComponent> entity, bool onlyAffectsMobs)
|
||||
{
|
||||
if (entity.Comp.OnlyAffectsMobs == onlyAffectsMobs)
|
||||
return;
|
||||
|
||||
entity.Comp.OnlyAffectsMobs = onlyAffectsMobs;
|
||||
Dirty(entity);
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,7 @@ public abstract class SharedInjectorSystem : EntitySystem
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
||||
if (!HasComp<ActorComponent>(args.User))
|
||||
return;
|
||||
var user = args.User;
|
||||
|
||||
var (_, component) = entity;
|
||||
|
||||
var min = component.MinimumTransferAmount;
|
||||
|
||||
@@ -298,7 +298,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: true);
|
||||
return _containers.CanInsert(usedUid, slot.ContainerSlot, assumeEmpty: swap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -270,7 +270,7 @@ public sealed class PullingSystem : EntitySystem
|
||||
}
|
||||
|
||||
Dirty(player, pullerComp);
|
||||
_throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false);
|
||||
_throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false, doSpin: false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
float pushbackRatio = PushbackDefault,
|
||||
bool recoil = true,
|
||||
bool animated = true,
|
||||
bool playSound = true)
|
||||
bool playSound = true,
|
||||
bool doSpin = true)
|
||||
{
|
||||
var thrownPos = _transform.GetMapCoordinates(uid);
|
||||
var mapPos = _transform.ToMapCoordinates(coordinates);
|
||||
@@ -57,7 +58,7 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
if (mapPos.MapId != thrownPos.MapId)
|
||||
return;
|
||||
|
||||
TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound);
|
||||
TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,6 +68,7 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
/// <param name="direction">A vector pointing from the entity to its destination.</param>
|
||||
/// <param name="strength">How much the direction vector should be multiplied for velocity.</param>
|
||||
/// <param name="pushbackRatio">The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced</param>
|
||||
/// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
|
||||
public void TryThrow(EntityUid uid,
|
||||
Vector2 direction,
|
||||
float strength = 1.0f,
|
||||
@@ -74,7 +76,8 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
float pushbackRatio = PushbackDefault,
|
||||
bool recoil = true,
|
||||
bool animated = true,
|
||||
bool playSound = true)
|
||||
bool playSound = true,
|
||||
bool doSpin = true)
|
||||
{
|
||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
if (!physicsQuery.TryGetComponent(uid, out var physics))
|
||||
@@ -90,7 +93,7 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
projectileQuery,
|
||||
strength,
|
||||
user,
|
||||
pushbackRatio, recoil: recoil, animated: animated, playSound: playSound);
|
||||
pushbackRatio, recoil: recoil, animated: animated, playSound: playSound, doSpin: doSpin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,6 +103,7 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
/// <param name="direction">A vector pointing from the entity to its destination.</param>
|
||||
/// <param name="strength">How much the direction vector should be multiplied for velocity.</param>
|
||||
/// <param name="pushbackRatio">The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced</param>
|
||||
/// <param name="doSpin">Whether spin will be applied to the thrown entity.</param>
|
||||
public void TryThrow(EntityUid uid,
|
||||
Vector2 direction,
|
||||
PhysicsComponent physics,
|
||||
@@ -110,7 +114,8 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
float pushbackRatio = PushbackDefault,
|
||||
bool recoil = true,
|
||||
bool animated = true,
|
||||
bool playSound = true)
|
||||
bool playSound = true,
|
||||
bool doSpin = true)
|
||||
{
|
||||
if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero)
|
||||
return;
|
||||
@@ -147,17 +152,20 @@ public sealed class ThrowingSystem : EntitySystem
|
||||
ThrowingAngleComponent? throwingAngle = null;
|
||||
|
||||
// Give it a l'il spin.
|
||||
if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity))
|
||||
if (doSpin)
|
||||
{
|
||||
_physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics);
|
||||
}
|
||||
else
|
||||
{
|
||||
Resolve(uid, ref throwingAngle, false);
|
||||
var gridRot = _transform.GetWorldRotation(transform.ParentUid);
|
||||
var angle = direction.ToWorldAngle() - gridRot;
|
||||
var offset = throwingAngle?.Angle ?? Angle.Zero;
|
||||
_transform.SetLocalRotation(uid, angle + offset);
|
||||
if (physics.InvI > 0f && (!TryComp(uid, out throwingAngle) || throwingAngle.AngularVelocity))
|
||||
{
|
||||
_physics.ApplyAngularImpulse(uid, ThrowAngularImpulse / physics.InvI, body: physics);
|
||||
}
|
||||
else
|
||||
{
|
||||
Resolve(uid, ref throwingAngle, false);
|
||||
var gridRot = _transform.GetWorldRotation(transform.ParentUid);
|
||||
var angle = direction.ToWorldAngle() - gridRot;
|
||||
var offset = throwingAngle?.Angle ?? Angle.Zero;
|
||||
_transform.SetLocalRotation(uid, angle + offset);
|
||||
}
|
||||
}
|
||||
|
||||
var throwEvent = new ThrownEvent(user, uid);
|
||||
|
||||
@@ -15,14 +15,7 @@ public sealed class WeldableSystem : EntitySystem
|
||||
[Dependency] private readonly SharedToolSystem _toolSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
||||
public bool IsWelded(EntityUid uid, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
return component.IsWelded;
|
||||
}
|
||||
private EntityQuery<WeldableComponent> _query;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -31,6 +24,13 @@ public sealed class WeldableSystem : EntitySystem
|
||||
SubscribeLocalEvent<WeldableComponent, WeldFinishedEvent>(OnWeldFinished);
|
||||
SubscribeLocalEvent<LayerChangeOnWeldComponent, WeldableChangedEvent>(OnWeldChanged);
|
||||
SubscribeLocalEvent<WeldableComponent, ExaminedEvent>(OnExamine);
|
||||
|
||||
_query = GetEntityQuery<WeldableComponent>();
|
||||
}
|
||||
|
||||
public bool IsWelded(EntityUid uid, WeldableComponent? component = null)
|
||||
{
|
||||
return _query.Resolve(uid, ref component, false) && component.IsWelded;
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, WeldableComponent component, ExaminedEvent args)
|
||||
@@ -49,7 +49,7 @@ public sealed class WeldableSystem : EntitySystem
|
||||
|
||||
private bool CanWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!_query.Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
// Other component systems
|
||||
@@ -63,7 +63,7 @@ public sealed class WeldableSystem : EntitySystem
|
||||
|
||||
private bool TryWeld(EntityUid uid, EntityUid tool, EntityUid user, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!_query.Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (!CanWeld(uid, tool, user, component))
|
||||
@@ -115,17 +115,13 @@ public sealed class WeldableSystem : EntitySystem
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
_appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded, appearance);
|
||||
if (_query.Resolve(uid, ref component))
|
||||
_appearance.SetData(uid, WeldableVisuals.IsWelded, component.IsWelded);
|
||||
}
|
||||
|
||||
public void SetWeldedState(EntityUid uid, bool state, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!_query.Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (component.IsWelded == state)
|
||||
@@ -141,7 +137,7 @@ public sealed class WeldableSystem : EntitySystem
|
||||
|
||||
public void SetWeldingTime(EntityUid uid, TimeSpan time, WeldableComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!_query.Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (component.WeldingTime.Equals(time))
|
||||
|
||||
Reference in New Issue
Block a user