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:
Remuchi
2024-03-30 11:40:01 +07:00
86 changed files with 1016 additions and 690 deletions

View File

@@ -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&lt;&lt;OppositeIndex(i) == (1&lt;&lt;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)

View 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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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))