Merge remote-tracking branch 'upstream/stable' into ed-27-05-2025-upstream-sync
# Conflicts: # .github/CODEOWNERS # Content.Client/Guidebook/Controls/GuideReagentReaction.xaml.cs # Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs # Content.Server/Procedural/DungeonJob/DungeonJob.OreDunGen.cs # Resources/Prototypes/Entities/Effects/chemistry_effects.yml # Resources/Prototypes/Entities/Mobs/Customization/Markings/human_hair.yml # Resources/Prototypes/GameRules/meteorswarms.yml # Resources/Prototypes/Procedural/dungeon_configs.yml
This commit is contained in:
@@ -10,9 +10,6 @@ namespace Content.Shared.Access.Components;
|
||||
[Access(typeof(SharedIdCardConsoleSystem))]
|
||||
public sealed partial class IdCardConsoleComponent : Component
|
||||
{
|
||||
public const int MaxFullNameLength = 30;
|
||||
public const int MaxJobTitleLength = 30;
|
||||
|
||||
public static string PrivilegedIdCardSlotId = "IdCardConsole-privilegedId";
|
||||
public static string TargetIdCardSlotId = "IdCardConsole-targetId";
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Globalization;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
@@ -8,6 +9,7 @@ using Content.Shared.Inventory;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -15,6 +17,7 @@ namespace Content.Shared.Access.Systems;
|
||||
|
||||
public abstract class SharedIdCardSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAccessSystem _access = default!;
|
||||
@@ -22,6 +25,10 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
// CCVar.
|
||||
private int _maxNameLength;
|
||||
private int _maxIdJobLength;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -29,6 +36,9 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
SubscribeLocalEvent<IdCardComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<TryGetIdentityShortInfoEvent>(OnTryGetIdentityShortInfo);
|
||||
SubscribeLocalEvent<EntityRenamedEvent>(OnRename);
|
||||
|
||||
Subs.CVar(_cfgManager, CCVars.MaxNameLength, value => _maxNameLength = value, true);
|
||||
Subs.CVar(_cfgManager, CCVars.MaxIdJobLength, value => _maxIdJobLength = value, true);
|
||||
}
|
||||
|
||||
private void OnRename(ref EntityRenamedEvent ev)
|
||||
@@ -131,8 +141,8 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
{
|
||||
jobTitle = jobTitle.Trim();
|
||||
|
||||
if (jobTitle.Length > IdCardConsoleComponent.MaxJobTitleLength)
|
||||
jobTitle = jobTitle[..IdCardConsoleComponent.MaxJobTitleLength];
|
||||
if (jobTitle.Length > _maxIdJobLength)
|
||||
jobTitle = jobTitle[.._maxIdJobLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -209,8 +219,8 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
if (!string.IsNullOrWhiteSpace(fullName))
|
||||
{
|
||||
fullName = fullName.Trim();
|
||||
if (fullName.Length > IdCardConsoleComponent.MaxFullNameLength)
|
||||
fullName = fullName[..IdCardConsoleComponent.MaxFullNameLength];
|
||||
if (fullName.Length > _maxNameLength)
|
||||
fullName = fullName[.._maxNameLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -51,13 +51,18 @@ public abstract class SharedAnomalySystem : EntitySystem
|
||||
return;
|
||||
|
||||
// anomalies are static by default, so we have set them to dynamic to be throwable
|
||||
_physics.SetBodyType(ent, BodyType.Dynamic, body: body);
|
||||
// only regular anomalies are static, so the check is meant to filter out things such as infection anomalies, which affect players
|
||||
if (TryComp<PhysicsComponent>(ent, out var physics) && physics.BodyType == BodyType.Static)
|
||||
_physics.SetBodyType(ent, BodyType.Dynamic, body: body);
|
||||
ChangeAnomalyStability(ent, Random.NextFloat(corePowered.StabilityPerThrow.X, corePowered.StabilityPerThrow.Y), ent.Comp);
|
||||
}
|
||||
|
||||
private void OnLand(Entity<AnomalyComponent> ent, ref LandEvent args)
|
||||
{
|
||||
// revert back to static
|
||||
// revert back to static, but only if the object was dynamic (such as thrown anomalies, but not anomaly infected players)
|
||||
if (!TryComp<PhysicsComponent>(ent, out var body) || body.BodyType != BodyType.Dynamic)
|
||||
return;
|
||||
|
||||
_physics.SetBodyType(ent, BodyType.Static);
|
||||
}
|
||||
|
||||
@@ -439,7 +444,7 @@ public abstract class SharedAnomalySystem : EntitySystem
|
||||
}
|
||||
|
||||
[DataRecord]
|
||||
public record struct AnomalySpawnSettings()
|
||||
public partial record struct AnomalySpawnSettings()
|
||||
{
|
||||
/// <summary>
|
||||
/// should entities block spawning?
|
||||
|
||||
@@ -91,7 +91,7 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder
|
||||
/// Increases explosion for each scale kPa above threshold.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float TankFragmentScale = 2 * Atmospherics.OneAtmosphere;
|
||||
public float TankFragmentScale = 2.25f * Atmospherics.OneAtmosphere;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId ToggleAction = "ActionToggleInternals";
|
||||
|
||||
@@ -37,7 +37,7 @@ public interface IAtmosDeviceData
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AirAlarmUIState : BoundUserInterfaceState
|
||||
{
|
||||
public AirAlarmUIState(string address, int deviceCount, float pressureAverage, float temperatureAverage, List<(string, IAtmosDeviceData)> deviceData, AirAlarmMode mode, AtmosAlarmType alarmType, bool autoMode)
|
||||
public AirAlarmUIState(string address, int deviceCount, float pressureAverage, float temperatureAverage, List<(string, IAtmosDeviceData)> deviceData, AirAlarmMode mode, AtmosAlarmType alarmType, bool autoMode, bool panicWireCut)
|
||||
{
|
||||
Address = address;
|
||||
DeviceCount = deviceCount;
|
||||
@@ -47,6 +47,7 @@ public sealed class AirAlarmUIState : BoundUserInterfaceState
|
||||
Mode = mode;
|
||||
AlarmType = alarmType;
|
||||
AutoMode = autoMode;
|
||||
PanicWireCut = panicWireCut;
|
||||
}
|
||||
|
||||
public string Address { get; }
|
||||
@@ -64,6 +65,7 @@ public sealed class AirAlarmUIState : BoundUserInterfaceState
|
||||
public AirAlarmMode Mode { get; }
|
||||
public AtmosAlarmType AlarmType { get; }
|
||||
public bool AutoMode { get; }
|
||||
public bool PanicWireCut { get; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using Content.Shared.Atmos.Piping.Binary.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Atmos.Piping.Binary.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component for manual atmospherics pumps that can open or close to let gas through.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedGasValveSystem))]
|
||||
public sealed partial class GasValveComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the valve is currently open and letting gas through.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool Open = true;
|
||||
|
||||
/// <summary>
|
||||
/// Inlet for the nodecontainer.
|
||||
/// </summary>
|
||||
[DataField("inlet")]
|
||||
public string InletName = "inlet";
|
||||
|
||||
/// <summary>
|
||||
/// Outlet for the nodecontainer.
|
||||
/// </summary>
|
||||
[DataField("outlet")]
|
||||
public string OutletName = "outlet";
|
||||
|
||||
/// <summary>
|
||||
/// Sound when <see cref="Open"/> is toggled.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier ValveSound = new SoundCollectionSpecifier("valveSqueak");
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Shared.Atmos.Piping.Binary.Systems;
|
||||
|
||||
public abstract class SharedGasValveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasValveComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<GasValveComponent, ActivateInWorldEvent>(OnActivate);
|
||||
SubscribeLocalEvent<GasValveComponent, ExaminedEvent>(OnExamined);
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<GasValveComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
// We call set in startup so it sets the appearance, node state, etc.
|
||||
Set(ent.Owner, ent.Comp, ent.Comp.Open);
|
||||
}
|
||||
|
||||
public virtual void Set(EntityUid uid, GasValveComponent component, bool value)
|
||||
{
|
||||
component.Open = value;
|
||||
Dirty(uid, component);
|
||||
|
||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
{
|
||||
_appearance.SetData(uid, FilterVisuals.Enabled, component.Open, appearance);
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle(EntityUid uid, GasValveComponent component)
|
||||
{
|
||||
Set(uid, component, !component.Open);
|
||||
}
|
||||
|
||||
private void OnActivate(Entity<GasValveComponent> ent, ref ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled || !args.Complex)
|
||||
return;
|
||||
|
||||
Toggle(ent.Owner, ent.Comp);
|
||||
_audio.PlayPredicted(ent.Comp.ValveSound, ent.Owner, args.User, AudioParams.Default.WithVariation(0.25f));
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<GasValveComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var valve = ent.Comp;
|
||||
if (!Transform(ent).Anchored)
|
||||
return;
|
||||
|
||||
if (Loc.TryGetString("gas-valve-system-examined", out var str,
|
||||
("statusColor", valve.Open ? "green" : "orange"),
|
||||
("open", valve.Open)))
|
||||
{
|
||||
args.PushMarkup(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Guidebook;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Atmos.Piping.Unary.Components
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
public sealed partial class GasThermoMachineComponent : Component
|
||||
{
|
||||
[DataField("inlet")]
|
||||
public string InletName = "pipe";
|
||||
|
||||
/// <summary>
|
||||
/// Current electrical power consumption, in watts. Increasing power increases the ability of the
|
||||
/// thermomachine to heat or cool air.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[GuidebookData]
|
||||
public float HeatCapacity = 5000;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public float TargetTemperature = Atmospherics.T20C;
|
||||
|
||||
/// <summary>
|
||||
/// Tolerance for temperature setpoint hysteresis.
|
||||
/// </summary>
|
||||
[GuidebookData]
|
||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public float TemperatureTolerance = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Implements setpoint hysteresis to prevent heater from rapidly cycling on and off at setpoint.
|
||||
/// If true, add Sign(Cp)*TemperatureTolerance to the temperature setpoint.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool HysteresisState;
|
||||
|
||||
/// <summary>
|
||||
/// Coefficient of performance. Output power / input power.
|
||||
/// Positive for heaters, negative for freezers.
|
||||
/// </summary>
|
||||
[DataField("coefficientOfPerformance")]
|
||||
public float Cp = 0.9f; // output power / input power, positive is heat
|
||||
|
||||
/// <summary>
|
||||
/// Current minimum temperature
|
||||
/// Ignored if heater.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[GuidebookData]
|
||||
public float MinTemperature = 73.15f;
|
||||
|
||||
/// <summary>
|
||||
/// Current maximum temperature
|
||||
/// Ignored if freezer.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[GuidebookData]
|
||||
public float MaxTemperature = 593.15f;
|
||||
|
||||
/// <summary>
|
||||
/// Last amount of energy added/removed from the attached pipe network
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float LastEnergyDelta;
|
||||
|
||||
/// <summary>
|
||||
/// An percentage of the energy change that is leaked into the surrounding environment rather than the inlet pipe.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[GuidebookData]
|
||||
public float EnergyLeakPercentage;
|
||||
|
||||
/// <summary>
|
||||
/// If true, heat is exclusively exchanged with the local atmosphere instead of the inlet pipe air
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Atmospheric;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ public sealed record GasThermoMachineData(float EnergyDelta);
|
||||
|
||||
[Serializable]
|
||||
[NetSerializable]
|
||||
public enum ThermomachineUiKey
|
||||
public enum ThermomachineUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
@@ -29,23 +29,3 @@ public sealed class GasThermomachineChangeTemperatureMessage : BoundUserInterfac
|
||||
Temperature = temperature;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[NetSerializable]
|
||||
public sealed class GasThermomachineBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public float MinTemperature { get; }
|
||||
public float MaxTemperature { get; }
|
||||
public float Temperature { get; }
|
||||
public bool Enabled { get; }
|
||||
public bool IsHeater { get; }
|
||||
|
||||
public GasThermomachineBoundUserInterfaceState(float minTemperature, float maxTemperature, float temperature, bool enabled, bool isHeater)
|
||||
{
|
||||
MinTemperature = minTemperature;
|
||||
MaxTemperature = maxTemperature;
|
||||
Temperature = temperature;
|
||||
Enabled = enabled;
|
||||
IsHeater = isHeater;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Content.Shared.Atmos.Piping.Unary.Components
|
||||
public ScrubberPumpDirection PumpDirection { get; set; } = ScrubberPumpDirection.Scrubbing;
|
||||
public float VolumeRate { get; set; } = 200f;
|
||||
public bool WideNet { get; set; } = false;
|
||||
public bool AirAlarmPanicWireCut { get; set; }
|
||||
|
||||
public static HashSet<Gas> DefaultFilterGases = new()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
|
||||
namespace Content.Shared.Atmos.Piping.Unary.Systems;
|
||||
|
||||
public abstract class SharedGasThermoMachineSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedPowerReceiverSystem _receiver = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, ExaminedEvent>(OnExamined);
|
||||
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineToggleMessage>(OnToggleMessage);
|
||||
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineChangeTemperatureMessage>(OnChangeTemperature);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, GasThermoMachineComponent thermoMachine, ExaminedEvent args)
|
||||
{
|
||||
if (Loc.TryGetString("gas-thermomachine-system-examined",
|
||||
out var str,
|
||||
("machineName", !IsHeater(thermoMachine) ? "freezer" : "heater"),
|
||||
("tempColor", !IsHeater(thermoMachine) ? "deepskyblue" : "red"),
|
||||
("temp", Math.Round(thermoMachine.TargetTemperature, 2))
|
||||
))
|
||||
{
|
||||
args.PushMarkup(str);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHeater(GasThermoMachineComponent comp)
|
||||
{
|
||||
return comp.Cp >= 0;
|
||||
}
|
||||
|
||||
private void OnToggleMessage(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineToggleMessage args)
|
||||
{
|
||||
var powerState = _receiver.TogglePower(uid, user: args.Actor);
|
||||
_adminLogger.Add(LogType.AtmosPowerChanged, $"{ToPrettyString(args.Actor)} turned {(powerState ? "On" : "Off")} {ToPrettyString(uid)}");
|
||||
DirtyUI(uid, thermoMachine);
|
||||
}
|
||||
|
||||
private void OnChangeTemperature(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineChangeTemperatureMessage args)
|
||||
{
|
||||
if (IsHeater(thermoMachine))
|
||||
thermoMachine.TargetTemperature = MathF.Min(args.Temperature, thermoMachine.MaxTemperature);
|
||||
else
|
||||
thermoMachine.TargetTemperature = MathF.Max(args.Temperature, thermoMachine.MinTemperature);
|
||||
thermoMachine.TargetTemperature = MathF.Max(thermoMachine.TargetTemperature, Atmospherics.TCMB);
|
||||
_adminLogger.Add(LogType.AtmosTemperatureChanged, $"{ToPrettyString(args.Actor)} set temperature on {ToPrettyString(uid)} to {thermoMachine.TargetTemperature}");
|
||||
Dirty(uid, thermoMachine);
|
||||
DirtyUI(uid, thermoMachine);
|
||||
}
|
||||
|
||||
protected virtual void DirtyUI(EntityUid uid, GasThermoMachineComponent? thermoMachine, UserInterfaceComponent? ui=null) {}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Atmos.Visuals
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum AtmosPlaqueVisuals
|
||||
{
|
||||
State
|
||||
}
|
||||
}
|
||||
40
Content.Shared/Bed/Components/HealOnBuckleComponent.cs
Normal file
40
Content.Shared/Bed/Components/HealOnBuckleComponent.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Bed.Components
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause, AutoGenerateComponentState]
|
||||
public sealed partial class HealOnBuckleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Damage to apply to entities that are strapped to this entity.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public DamageSpecifier Damage = default!;
|
||||
|
||||
/// <summary>
|
||||
/// How frequently the damage should be applied, in seconds.
|
||||
/// </summary>
|
||||
[DataField(required: false)]
|
||||
public float HealTime = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Damage multiplier that gets applied if the entity is sleeping.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float SleepMultiplier = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// Next time that <see cref="Damage"/> will be applied.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
|
||||
public TimeSpan NextHealTime = TimeSpan.Zero; //Next heal
|
||||
|
||||
/// <summary>
|
||||
/// Action for the attached entity to be able to sleep.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? SleepAction;
|
||||
}
|
||||
}
|
||||
7
Content.Shared/Bed/Components/HealOnBuckleHealing.cs
Normal file
7
Content.Shared/Bed/Components/HealOnBuckleHealing.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Bed.Components;
|
||||
|
||||
// TODO rename this component
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class HealOnBuckleHealingComponent : Component;
|
||||
@@ -7,7 +7,6 @@ using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
||||
49
Content.Shared/Bed/SharedBedSystem.cs
Normal file
49
Content.Shared/Bed/SharedBedSystem.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Bed.Components;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Bed;
|
||||
|
||||
public abstract class SharedBedSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||
[Dependency] private readonly ActionContainerSystem _actConts = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HealOnBuckleComponent, MapInitEvent>(OnHealMapInit);
|
||||
SubscribeLocalEvent<HealOnBuckleComponent, StrappedEvent>(OnStrapped);
|
||||
SubscribeLocalEvent<HealOnBuckleComponent, UnstrappedEvent>(OnUnstrapped);
|
||||
}
|
||||
|
||||
private void OnHealMapInit(Entity<HealOnBuckleComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_actConts.EnsureAction(ent.Owner, ref ent.Comp.SleepAction, SleepingSystem.SleepActionId);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnStrapped(Entity<HealOnBuckleComponent> bed, ref StrappedEvent args)
|
||||
{
|
||||
EnsureComp<HealOnBuckleHealingComponent>(bed);
|
||||
bed.Comp.NextHealTime = Timing.CurTime + TimeSpan.FromSeconds(bed.Comp.HealTime);
|
||||
_actionsSystem.AddAction(args.Buckle, ref bed.Comp.SleepAction, SleepingSystem.SleepActionId, bed);
|
||||
Dirty(bed);
|
||||
|
||||
// Single action entity, cannot strap multiple entities to the same bed.
|
||||
DebugTools.AssertEqual(args.Strap.Comp.BuckledEntities.Count, 1);
|
||||
}
|
||||
|
||||
private void OnUnstrapped(Entity<HealOnBuckleComponent> bed, ref UnstrappedEvent args)
|
||||
{
|
||||
_actionsSystem.RemoveAction(args.Buckle, bed.Comp.SleepAction);
|
||||
_sleepingSystem.TryWaking(args.Buckle.Owner);
|
||||
RemComp<HealOnBuckleHealingComponent>(bed);
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,8 @@ public sealed class ProximityBeeperSystem : EntitySystem
|
||||
{
|
||||
if (!TryComp<BeeperComponent>(owner, out var beeper))
|
||||
return;
|
||||
if (args.Target == null)
|
||||
{
|
||||
_beeper.SetMute(owner, true, beeper);
|
||||
return;
|
||||
}
|
||||
|
||||
_beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Range, beeper);
|
||||
_beeper.SetMute(owner, false, beeper);
|
||||
_beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Comp.Range, beeper);
|
||||
}
|
||||
|
||||
private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Shared.CCVar;
|
||||
|
||||
@@ -72,4 +73,24 @@ public sealed partial class CCVars
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> DiscordWatchlistConnectionBufferTime =
|
||||
CVarDef.Create("discord.watchlist_connection_buffer_time", 5f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// URL of the Discord webhook which will receive station news acticles at the round end.
|
||||
/// If left empty, disables the webhook.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> DiscordNewsWebhook =
|
||||
CVarDef.Create("discord.news_webhook", string.Empty, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// HEX color of station news discord webhook's embed.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> DiscordNewsWebhookEmbedColor =
|
||||
CVarDef.Create("discord.news_webhook_embed_color", Color.LawnGreen.ToHex(), CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not articles should be sent mid-round instead of all at once at the round's end
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> DiscordNewsWebhookSendDuringRound =
|
||||
CVarDef.Create("discord.news_webhook_send_during_round", false, CVar.SERVERONLY);
|
||||
|
||||
}
|
||||
|
||||
@@ -11,11 +11,35 @@ public sealed partial class CCVars
|
||||
CVarDef.Create("ic.restricted_names", true, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Allows flavor text (character descriptions)
|
||||
/// Sets the maximum IC name length.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> MaxNameLength =
|
||||
CVarDef.Create("ic.name_length", 32, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum name length for a loadout name (e.g. cyborg name).
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> MaxLoadoutNameLength =
|
||||
CVarDef.Create("ic.loadout_name_length", 32, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Allows flavor text (character descriptions).
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> FlavorText =
|
||||
CVarDef.Create("ic.flavor_text", false, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum length for flavor text (character descriptions).
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> MaxFlavorTextLength =
|
||||
CVarDef.Create("ic.flavor_text_length", 512, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum character length of a job on an ID.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> MaxIdJobLength =
|
||||
CVarDef.Create("ic.id_job_length", 30, CVar.SERVER | CVar.REPLICATED);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a period at the end of a sentence if the sentence ends in a letter.
|
||||
/// </summary>
|
||||
|
||||
@@ -194,4 +194,92 @@ public sealed partial class CCVars
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> GridImpulseMultiplier =
|
||||
CVarDef.Create("shuttle.grid_impulse_multiplier", 0.01f, CVar.SERVERONLY);
|
||||
|
||||
#region impacts
|
||||
|
||||
/// <summary>
|
||||
/// Whether shuttle impacts should do anything beyond produce a sound.
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<bool> ImpactEnabled =
|
||||
CVarDef.Create("shuttle.impact.enabled", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum impact inertia to trigger special shuttle impact behaviors when impacting slower than MinimumImpactVelocity.
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> MinimumImpactInertia =
|
||||
CVarDef.Create("shuttle.impact.minimum_inertia", 5f * 50f, CVar.SERVERONLY); // 100tile grid (cargo shuttle) going at 5 m/s
|
||||
|
||||
/// <summary>
|
||||
/// Minimum velocity difference between 2 bodies for a shuttle impact to be guaranteed to trigger any special behaviors like damage.
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> MinimumImpactVelocity =
|
||||
CVarDef.Create("shuttle.impact.minimum_velocity", 15f, CVar.SERVERONLY); // needed so that random space debris can be rammed
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier of Kinetic energy required to dismantle a single tile in relation to its mass
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> TileBreakEnergyMultiplier =
|
||||
CVarDef.Create("shuttle.impact.tile_break_energy", 3000f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier of damage done to entities on colliding areas
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactDamageMultiplier =
|
||||
CVarDef.Create("shuttle.impact.damage_multiplier", 0.00005f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier of additional structural damage to do
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactStructuralDamage =
|
||||
CVarDef.Create("shuttle.impact.structural_damage", 5f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Kinetic energy required to spawn sparks
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> SparkEnergy =
|
||||
CVarDef.Create("shuttle.impact.spark_energy", 2000000f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Area to consider for impact calculations
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactRadius =
|
||||
CVarDef.Create("shuttle.impact.radius", 4f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Affects slowdown on impact
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactSlowdown =
|
||||
CVarDef.Create("shuttle.impact.slowdown", 8f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum velocity change from impact for special throw effects (e.g. stuns, beakers breaking) to occur
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactMinThrowVelocity =
|
||||
CVarDef.Create("shuttle.impact.min_throw_velocity", 1f, CVar.SERVERONLY); // due to how it works this is about 16 m/s for cargo shuttle
|
||||
|
||||
/// <summary>
|
||||
/// Affects how much damage reduction to give to grids with higher mass
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactMassBias =
|
||||
CVarDef.Create("shuttle.impact.mass_bias", 0.65f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// How much should total grid inertia affect our collision damage
|
||||
/// </summary>
|
||||
[CVarControl(AdminFlags.VarEdit)]
|
||||
public static readonly CVarDef<float> ImpactInertiaScaling =
|
||||
CVarDef.Create("shuttle.impact.inertia_scaling", 0.5f, CVar.SERVERONLY);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Cargo.BUI;
|
||||
@@ -10,13 +12,15 @@ public sealed class CargoConsoleInterfaceState : BoundUserInterfaceState
|
||||
public int Capacity;
|
||||
public NetEntity Station;
|
||||
public List<CargoOrderData> Orders;
|
||||
public List<ProtoId<CargoProductPrototype>> Products;
|
||||
|
||||
public CargoConsoleInterfaceState(string name, int count, int capacity, NetEntity station, List<CargoOrderData> orders)
|
||||
public CargoConsoleInterfaceState(string name, int count, int capacity, NetEntity station, List<CargoOrderData> orders, List<ProtoId<CargoProductPrototype>> products)
|
||||
{
|
||||
Name = name;
|
||||
Count = count;
|
||||
Capacity = capacity;
|
||||
Station = station;
|
||||
Orders = orders;
|
||||
Products = products;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,13 @@ public sealed partial class CargoOrderConsoleComponent : Component
|
||||
/// All of the <see cref="CargoProductPrototype.Group"/>s that are supported.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<string> AllowedGroups = new() { "market" };
|
||||
public List<ProtoId<CargoMarketPrototype>> AllowedGroups = new()
|
||||
{
|
||||
"market",
|
||||
"SalvageJobReward2",
|
||||
"SalvageJobReward3",
|
||||
"SalvageJobRewardMAX",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Access needed to toggle the limit on this console.
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed partial class CargoAccountPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Full IC name of the account.
|
||||
|
||||
14
Content.Shared/Cargo/Prototypes/CargoBountyGroupPrototype.cs
Normal file
14
Content.Shared/Cargo/Prototypes/CargoBountyGroupPrototype.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Cargo.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// Used to categorize bounties for different purposes
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class CargoBountyGroupPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Cargo.Prototypes;
|
||||
|
||||
@@ -39,6 +40,18 @@ public sealed partial class CargoBountyPrototype : IPrototype
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string IdPrefix = "NT";
|
||||
|
||||
/// <summary>
|
||||
/// A group used for categorizing this bounty.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CargoBountyGroupPrototype> Group = "StationBounty";
|
||||
|
||||
/// <summary>
|
||||
/// Optional sprite representing this bounty.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SpriteSpecifier? Sprite;
|
||||
}
|
||||
|
||||
[DataDefinition, Serializable, NetSerializable]
|
||||
|
||||
14
Content.Shared/Cargo/Prototypes/CargoMarketPrototype.cs
Normal file
14
Content.Shared/Cargo/Prototypes/CargoMarketPrototype.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Cargo.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a "market" that a cargo computer can access and make orders from.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class CargoMarketPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
@@ -93,6 +93,6 @@ namespace Content.Shared.Cargo.Prototypes
|
||||
/// The prototype group of the product. (e.g. Contraband)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Group { get; private set; } = "market";
|
||||
public ProtoId<CargoMarketPrototype> Group { get; private set; } = "market";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface INanoTaskUiMessagePayload
|
||||
/// Dispatched when a new task is created
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskAddTask : INanoTaskUiMessagePayload
|
||||
public sealed partial class NanoTaskAddTask : INanoTaskUiMessagePayload
|
||||
{
|
||||
/// <summary>
|
||||
/// The newly created task
|
||||
@@ -30,7 +30,7 @@ public sealed class NanoTaskAddTask : INanoTaskUiMessagePayload
|
||||
/// Dispatched when an existing task is modified
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskUpdateTask : INanoTaskUiMessagePayload
|
||||
public sealed partial class NanoTaskUpdateTask : INanoTaskUiMessagePayload
|
||||
{
|
||||
/// <summary>
|
||||
/// The task that was updated and its ID
|
||||
@@ -47,7 +47,7 @@ public sealed class NanoTaskUpdateTask : INanoTaskUiMessagePayload
|
||||
/// Dispatched when an existing task is deleted
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskDeleteTask : INanoTaskUiMessagePayload
|
||||
public sealed partial class NanoTaskDeleteTask : INanoTaskUiMessagePayload
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the task to delete
|
||||
@@ -64,7 +64,7 @@ public sealed class NanoTaskDeleteTask : INanoTaskUiMessagePayload
|
||||
/// Dispatched when a task is requested to be printed
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskPrintTask : INanoTaskUiMessagePayload
|
||||
public sealed partial class NanoTaskPrintTask : INanoTaskUiMessagePayload
|
||||
{
|
||||
/// <summary>
|
||||
/// The NanoTask to print
|
||||
|
||||
@@ -17,7 +17,7 @@ public enum NanoTaskPriority : byte
|
||||
/// The data relating to a single NanoTask item, but not its identifier
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskItem
|
||||
public sealed partial class NanoTaskItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum length of the Description and TaskIsFor fields
|
||||
@@ -61,7 +61,7 @@ public sealed class NanoTaskItem
|
||||
/// Pairs a NanoTask item and its identifier
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, DataRecord]
|
||||
public sealed class NanoTaskItemAndId
|
||||
public sealed partial class NanoTaskItemAndId
|
||||
{
|
||||
public readonly int Id;
|
||||
public readonly NanoTaskItem Data;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SolutionScannerComponent : Component
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Allows an entity to examine reagents inside of containers, puddles and similiar via the examine verb.
|
||||
/// Works when added either directly to an entity or to piece of clothing worn by that entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class SolutionScannerComponent : Component;
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Shared.Chemistry.Dispenser
|
||||
{
|
||||
/// <summary>
|
||||
/// Is simply a list of reagents defined in yaml. This can then be set as a
|
||||
/// <see cref="SharedReagentDispenserComponent"/>s <c>pack</c> value (also in yaml),
|
||||
/// to define which reagents it's able to dispense. Based off of how vending
|
||||
/// machines define their inventory.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, Prototype]
|
||||
public sealed partial class ReagentDispenserInventoryPrototype : IPrototype
|
||||
{
|
||||
[DataField("inventory", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||
public List<string> Inventory = new();
|
||||
|
||||
[ViewVariables, IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
public float PricePerUnit;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6));
|
||||
public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepPuddle");
|
||||
|
||||
public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager, List<ReagentData>? data)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
@@ -66,11 +67,25 @@ namespace Content.Shared.Chemistry
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly string SlotId;
|
||||
public readonly ItemStorageLocation StorageLocation;
|
||||
|
||||
public ReagentDispenserDispenseReagentMessage(string slotId)
|
||||
public ReagentDispenserDispenseReagentMessage(ItemStorageLocation storageLocation)
|
||||
{
|
||||
SlotId = slotId;
|
||||
StorageLocation = storageLocation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message sent by the user interface to ask the reagent dispenser to eject a container
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentDispenserEjectContainerMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly ItemStorageLocation StorageLocation;
|
||||
|
||||
public ReagentDispenserEjectContainerMessage(ItemStorageLocation storageLocation)
|
||||
{
|
||||
StorageLocation = storageLocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,9 +109,9 @@ namespace Content.Shared.Chemistry
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentInventoryItem(string storageSlotId, string reagentLabel, FixedPoint2 quantity, Color reagentColor)
|
||||
public sealed class ReagentInventoryItem(ItemStorageLocation storageLocation, string reagentLabel, FixedPoint2 quantity, Color reagentColor)
|
||||
{
|
||||
public string StorageSlotId = storageSlotId;
|
||||
public ItemStorageLocation StorageLocation = storageLocation;
|
||||
public string ReagentLabel = reagentLabel;
|
||||
public FixedPoint2 Quantity = quantity;
|
||||
public Color ReagentColor = reagentColor;
|
||||
|
||||
@@ -19,11 +19,11 @@ public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPr
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[ParentDataField(typeof(PrototypeIdArraySerializer<CloningSettingsPrototype>))]
|
||||
public string[]? Parents { get; }
|
||||
public string[]? Parents { get; private set; }
|
||||
|
||||
[AbstractDataField]
|
||||
[NeverPushInheritance]
|
||||
public bool Abstract { get; }
|
||||
public bool Abstract { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if cloning can be prevented by traits etc.
|
||||
|
||||
@@ -65,14 +65,14 @@ public abstract class ClothingSystem : EntitySystem
|
||||
if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, checkDoafter: true))
|
||||
continue;
|
||||
|
||||
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
||||
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
||||
continue;
|
||||
|
||||
_handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
||||
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Content.Shared.Clothing.EntitySystems;
|
||||
|
||||
public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly ClothingSystem _clothingSystem = default!;
|
||||
[Dependency] private readonly ContrabandSystem _contraband = default!;
|
||||
@@ -66,7 +65,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
|
||||
// item sprite logic
|
||||
if (TryComp(uid, out ItemComponent? item) &&
|
||||
proto.TryGetComponent(out ItemComponent? otherItem, _factory))
|
||||
proto.TryGetComponent(out ItemComponent? otherItem, Factory))
|
||||
{
|
||||
_itemSystem.CopyVisuals(uid, otherItem, item);
|
||||
}
|
||||
@@ -126,7 +125,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
return false;
|
||||
|
||||
// check if it is marked as valid chameleon target
|
||||
if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, WhitelistChameleonTag))
|
||||
if (!proto.TryGetComponent(out TagComponent? tag, Factory) || !_tag.HasTag(tag, WhitelistChameleonTag))
|
||||
return false;
|
||||
|
||||
if (requiredTag != null && !_tag.HasTag(tag, requiredTag))
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace Content.Shared.Cuffs
|
||||
// TODO remove all the IsServer() checks.
|
||||
public abstract partial class SharedCuffableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
@@ -146,7 +145,7 @@ namespace Content.Shared.Cuffs
|
||||
|
||||
private void OnStartup(EntityUid uid, CuffableComponent component, ComponentInit args)
|
||||
{
|
||||
component.Container = _container.EnsureContainer<Container>(uid, _componentFactory.GetComponentName(component.GetType()));
|
||||
component.Container = _container.EnsureContainer<Container>(uid, Factory.GetComponentName(component.GetType()));
|
||||
}
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, CuffableComponent component, RejuvenateEvent args)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.StatusEffect;
|
||||
@@ -18,6 +19,7 @@ public abstract class SharedGodmodeSystem : EntitySystem
|
||||
SubscribeLocalEvent<GodmodeComponent, BeforeStatusEffectAddedEvent>(OnBeforeStatusEffect);
|
||||
SubscribeLocalEvent<GodmodeComponent, BeforeStaminaDamageEvent>(OnBeforeStaminaDamage);
|
||||
SubscribeLocalEvent<GodmodeComponent, SlipAttemptEvent>(OnSlipAttempt);
|
||||
SubscribeLocalEvent<GodmodeComponent, DestructionAttemptEvent>(OnDestruction);
|
||||
}
|
||||
|
||||
private void OnSlipAttempt(EntityUid uid, GodmodeComponent component, SlipAttemptEvent args)
|
||||
@@ -40,6 +42,11 @@ public abstract class SharedGodmodeSystem : EntitySystem
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnDestruction(Entity<GodmodeComponent> ent, ref DestructionAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
public virtual void EnableGodmode(EntityUid uid, GodmodeComponent? godmode = null)
|
||||
{
|
||||
godmode ??= EnsureComp<GodmodeComponent>(uid);
|
||||
|
||||
65
Content.Shared/Delivery/DeliveryBombComponent.cs
Normal file
65
Content.Shared/Delivery/DeliveryBombComponent.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Delivery;
|
||||
|
||||
/// <summary>
|
||||
/// Component given to deliveries.
|
||||
/// This delivery will "prime" based on circumstances defined in the datafield.
|
||||
/// When primed, it will attempt to explode every few seconds, with the chance increasing each time it fails to do so.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||
[Access(typeof(DeliveryModifierSystem))]
|
||||
public sealed partial class DeliveryBombComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How often will this bomb retry to explode.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan ExplosionRetryDelay = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the next retry will happen
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||
public TimeSpan NextExplosionRetry;
|
||||
|
||||
/// <summary>
|
||||
/// The chance this bomb explodes each time it attempts to do so.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float ExplosionChance = 0.01f;
|
||||
|
||||
/// <summary>
|
||||
/// How much should the chance of explosion increase each failed retry?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ExplosionChanceRetryIncrease = 0.01f;
|
||||
|
||||
/// <summary>
|
||||
/// Should this bomb get primed when the delivery is unlocked?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PrimeOnUnlock = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should this bomb get primed when the delivery is broken?
|
||||
/// Requires to be fragile as well.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PrimeOnBreakage = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should this bomb get primed when the delivery expires?
|
||||
/// Requires to be priority as well.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PrimeOnExpire = true;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier to choose when a crazy person actually opens it.
|
||||
/// Multiplicative, not additive.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float SpesoMultiplier = 1.5f;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Content.Shared.NameModifier.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -14,8 +18,11 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
|
||||
[Dependency] private readonly SharedDeliverySystem _delivery = default!;
|
||||
[Dependency] private readonly SharedExplosionSystem _explosion = default!;
|
||||
[Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -33,6 +40,14 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
|
||||
SubscribeLocalEvent<DeliveryFragileComponent, BreakageEventArgs>(OnFragileBreakage);
|
||||
SubscribeLocalEvent<DeliveryFragileComponent, ExaminedEvent>(OnFragileExamine);
|
||||
SubscribeLocalEvent<DeliveryFragileComponent, GetDeliveryMultiplierEvent>(OnGetFragileMultiplier);
|
||||
|
||||
SubscribeLocalEvent<DeliveryBombComponent, ComponentStartup>(OnExplosiveStartup);
|
||||
SubscribeLocalEvent<PrimedDeliveryBombComponent, MapInitEvent>(OnPrimedExplosiveMapInit);
|
||||
SubscribeLocalEvent<DeliveryBombComponent, ExaminedEvent>(OnExplosiveExamine);
|
||||
SubscribeLocalEvent<DeliveryBombComponent, GetDeliveryMultiplierEvent>(OnGetExplosiveMultiplier);
|
||||
SubscribeLocalEvent<DeliveryBombComponent, DeliveryUnlockedEvent>(OnExplosiveUnlock);
|
||||
SubscribeLocalEvent<DeliveryBombComponent, DeliveryPriorityExpiredEvent>(OnExplosiveExpire);
|
||||
SubscribeLocalEvent<DeliveryBombComponent, BreakageEventArgs>(OnExplosiveBreak);
|
||||
}
|
||||
|
||||
#region Random
|
||||
@@ -119,12 +134,80 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Explosive
|
||||
private void OnExplosiveStartup(Entity<DeliveryBombComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
_delivery.UpdateBombVisuals(ent);
|
||||
}
|
||||
|
||||
private void OnPrimedExplosiveMapInit(Entity<PrimedDeliveryBombComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
if (!TryComp<DeliveryBombComponent>(ent, out var bomb))
|
||||
return;
|
||||
|
||||
bomb.NextExplosionRetry = _timing.CurTime;
|
||||
}
|
||||
|
||||
private void OnExplosiveExamine(Entity<DeliveryBombComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var trueName = _nameModifier.GetBaseName(ent.Owner);
|
||||
|
||||
var isPrimed = HasComp<PrimedDeliveryBombComponent>(ent);
|
||||
|
||||
if (isPrimed)
|
||||
args.PushMarkup(Loc.GetString("delivery-bomb-primed-examine", ("type", trueName)));
|
||||
else
|
||||
args.PushMarkup(Loc.GetString("delivery-bomb-examine", ("type", trueName)));
|
||||
}
|
||||
|
||||
private void OnGetExplosiveMultiplier(Entity<DeliveryBombComponent> ent, ref GetDeliveryMultiplierEvent args)
|
||||
{
|
||||
// Big danger for big rewards
|
||||
args.MultiplicativeMultiplier += ent.Comp.SpesoMultiplier;
|
||||
}
|
||||
|
||||
private void OnExplosiveUnlock(Entity<DeliveryBombComponent> ent, ref DeliveryUnlockedEvent args)
|
||||
{
|
||||
if (!ent.Comp.PrimeOnUnlock)
|
||||
return;
|
||||
|
||||
PrimeBombDelivery(ent);
|
||||
}
|
||||
|
||||
private void OnExplosiveExpire(Entity<DeliveryBombComponent> ent, ref DeliveryPriorityExpiredEvent args)
|
||||
{
|
||||
if (!ent.Comp.PrimeOnExpire)
|
||||
return;
|
||||
|
||||
PrimeBombDelivery(ent);
|
||||
}
|
||||
|
||||
private void OnExplosiveBreak(Entity<DeliveryBombComponent> ent, ref BreakageEventArgs args)
|
||||
{
|
||||
if (!ent.Comp.PrimeOnBreakage)
|
||||
return;
|
||||
|
||||
PrimeBombDelivery(ent);
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public void PrimeBombDelivery(Entity<DeliveryBombComponent> ent)
|
||||
{
|
||||
EnsureComp<PrimedDeliveryBombComponent>(ent);
|
||||
|
||||
_delivery.UpdateBombVisuals(ent);
|
||||
|
||||
_ambientSound.SetAmbience(ent, true);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update Loops
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
UpdatePriorty(frameTime);
|
||||
UpdateBomb(frameTime);
|
||||
}
|
||||
|
||||
private void UpdatePriorty(float frameTime)
|
||||
@@ -148,6 +231,27 @@ public sealed partial class DeliveryModifierSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBomb(float frameTime)
|
||||
{
|
||||
var bombQuery = EntityQueryEnumerator<PrimedDeliveryBombComponent, DeliveryBombComponent>();
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
while (bombQuery.MoveNext(out var uid, out _, out var bombData))
|
||||
{
|
||||
if (bombData.NextExplosionRetry > curTime)
|
||||
continue;
|
||||
|
||||
bombData.NextExplosionRetry += bombData.ExplosionRetryDelay;
|
||||
|
||||
// Explosions cannot be predicted.
|
||||
if (_net.IsServer && _random.NextFloat() < bombData.ExplosionChance)
|
||||
_explosion.TriggerExplosive(uid);
|
||||
|
||||
bombData.ExplosionChance += bombData.ExplosionChanceRetryIncrease;
|
||||
Dirty(uid, bombData);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ public enum DeliveryVisuals : byte
|
||||
IsTrash,
|
||||
IsBroken,
|
||||
IsFragile,
|
||||
IsBomb,
|
||||
PriorityState,
|
||||
JobIcon,
|
||||
}
|
||||
@@ -21,6 +22,14 @@ public enum DeliveryPriorityState : byte
|
||||
Inactive,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DeliveryBombState : byte
|
||||
{
|
||||
Off,
|
||||
Inactive,
|
||||
Primed,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DeliverySpawnerVisuals : byte
|
||||
{
|
||||
|
||||
11
Content.Shared/Delivery/PrimedDeliveryBombComponent.cs
Normal file
11
Content.Shared/Delivery/PrimedDeliveryBombComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Delivery;
|
||||
|
||||
/// <summary>
|
||||
/// Component given to deliveries.
|
||||
/// Indicates this bomb delivery is primed.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(DeliveryModifierSystem))]
|
||||
public sealed partial class PrimedDeliveryBombComponent : Component;
|
||||
@@ -259,6 +259,13 @@ public abstract class SharedDeliverySystem : EntitySystem
|
||||
_appearance.SetData(ent, DeliveryVisuals.IsFragile, isFragile);
|
||||
}
|
||||
|
||||
public void UpdateBombVisuals(Entity<DeliveryBombComponent> ent)
|
||||
{
|
||||
var isPrimed = HasComp<PrimedDeliveryBombComponent>(ent);
|
||||
|
||||
_appearance.SetData(ent, DeliveryVisuals.IsBomb, isPrimed ? DeliveryBombState.Primed : DeliveryBombState.Inactive);
|
||||
}
|
||||
|
||||
protected void UpdateDeliverySpawnerVisuals(EntityUid uid, int contents)
|
||||
{
|
||||
_appearance.SetData(uid, DeliverySpawnerVisuals.Contents, contents > 0);
|
||||
|
||||
@@ -5,12 +5,18 @@ public abstract class SharedDestructibleSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Force entity to be destroyed and deleted.
|
||||
/// </summary>
|
||||
public void DestroyEntity(EntityUid owner)
|
||||
public bool DestroyEntity(EntityUid owner)
|
||||
{
|
||||
var eventArgs = new DestructionEventArgs();
|
||||
var ev = new DestructionAttemptEvent();
|
||||
RaiseLocalEvent(owner, ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
|
||||
var eventArgs = new DestructionEventArgs();
|
||||
RaiseLocalEvent(owner, eventArgs);
|
||||
|
||||
QueueDel(owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -23,6 +29,14 @@ public abstract class SharedDestructibleSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised before an entity is about to be destroyed and deleted
|
||||
/// </summary>
|
||||
public sealed class DestructionAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when entity is destroyed and about to be deleted.
|
||||
/// </summary>
|
||||
|
||||
@@ -40,3 +40,15 @@ public enum LogicGateLayers : byte
|
||||
InputB,
|
||||
Output
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The possible states of a logic-capable signal.
|
||||
/// Stored in network payload data of device network messages.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public enum SignalState : byte
|
||||
{
|
||||
Momentary, // Instantaneous pulse high, compatibility behavior
|
||||
Low,
|
||||
High
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared.EntityList;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class EntityLootTablePrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("entries")]
|
||||
public ImmutableList<EntitySpawnEntry> Entries = ImmutableList<EntitySpawnEntry>.Empty;
|
||||
|
||||
/// <inheritdoc cref="EntitySpawnCollection.GetSpawns"/>
|
||||
public List<string> GetSpawns(IRobustRandom random)
|
||||
{
|
||||
return EntitySpawnCollection.GetSpawns(Entries, random);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ public sealed class EntityTableSystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public IEnumerable<EntProtoId> GetSpawns(EntityTablePrototype entTableProto, System.Random? rand = null)
|
||||
{
|
||||
// convenient
|
||||
return GetSpawns(entTableProto.Table, rand);
|
||||
}
|
||||
|
||||
public IEnumerable<EntProtoId> GetSpawns(EntityTableSelector? table, System.Random? rand = null)
|
||||
{
|
||||
if (table == null)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Globalization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.EntityTable.ValueSelector;
|
||||
|
||||
[TypeSerializer]
|
||||
public sealed class NumberSelectorTypeSerializer :
|
||||
ITypeReader<NumberSelector, ValueDataNode>
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
// ConstantNumberSelector validation
|
||||
if (int.TryParse(node.Value, out _))
|
||||
return new ValidatedValueNode(node);
|
||||
|
||||
// RangeNumberSelector validation
|
||||
if (VectorSerializerUtility.TryParseArgs(node.Value, 2, out _))
|
||||
{
|
||||
return new ValidatedValueNode(node);
|
||||
}
|
||||
|
||||
return new ErrorNode(node, "Custom validation not supported! Please specify the type manually!");
|
||||
}
|
||||
|
||||
public NumberSelector Read(ISerializationManager serializationManager,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null,
|
||||
ISerializationManager.InstantiationDelegate<NumberSelector>? instanceProvider = null)
|
||||
{
|
||||
var type = typeof(NumberSelector);
|
||||
|
||||
if (int.TryParse(node.Value, out var result))
|
||||
return new ConstantNumberSelector(result);
|
||||
|
||||
if (VectorSerializerUtility.TryParseArgs(node.Value, 2, out var args))
|
||||
{
|
||||
var x = int.Parse(args[0], CultureInfo.InvariantCulture);
|
||||
var y = int.Parse(args[1], CultureInfo.InvariantCulture);
|
||||
return new RangeNumberSelector(new Vector2i(x, y));
|
||||
}
|
||||
|
||||
return (NumberSelector) serializationManager.Read(type, node, context)!;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ public sealed partial class RangeNumberSelector : NumberSelector
|
||||
[DataField]
|
||||
public Vector2i Range = new(1, 1);
|
||||
|
||||
public RangeNumberSelector(Vector2i range)
|
||||
{
|
||||
Range = range;
|
||||
}
|
||||
|
||||
public override int Get(System.Random rand)
|
||||
{
|
||||
// rand.Next() is inclusive on the first number and exclusive on the second number,
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace Content.Shared.Examine
|
||||
{
|
||||
public abstract partial class ExamineSystemShared : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
|
||||
public const string DefaultIconTexture = "/Textures/Interface/examine-star.png";
|
||||
|
||||
public override void Initialize()
|
||||
@@ -55,7 +53,7 @@ namespace Content.Shared.Examine
|
||||
{
|
||||
foreach (var comp in components)
|
||||
{
|
||||
if (!_componentFactory.TryGetRegistration(comp, out var componentRegistration))
|
||||
if (!Factory.TryGetRegistration(comp, out var componentRegistration))
|
||||
continue;
|
||||
|
||||
if (!HasComp(uid, componentRegistration.Type))
|
||||
@@ -117,7 +115,7 @@ namespace Content.Shared.Examine
|
||||
if (TryComp<GroupExamineComponent>(verbsEvent.Target, out var groupExamine))
|
||||
{
|
||||
// Make sure we have the component name as a string
|
||||
var componentName = _componentFactory.GetComponentName(component.GetType());
|
||||
var componentName = Factory.GetComponentName(component.GetType());
|
||||
|
||||
foreach (var examineGroup in groupExamine.Group)
|
||||
{
|
||||
@@ -177,7 +175,7 @@ namespace Content.Shared.Examine
|
||||
/// </summary>
|
||||
public void AddDetailedExamineVerb(GetVerbsEvent<ExamineVerb> verbsEvent, Component component, FormattedMessage message, string verbText, string iconTexture = DefaultIconTexture, string hoverMessage = "", bool isHoverExamine = false)
|
||||
{
|
||||
var componentName = _componentFactory.GetComponentName(component.GetType());
|
||||
var componentName = Factory.GetComponentName(component.GetType());
|
||||
AddDetailedExamineVerb(verbsEvent, component, new ExamineEntry(componentName, 0f, message), verbText, iconTexture, hoverMessage, isHoverExamine);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Explosion.Components.OnTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a GasMixture that will release its contents to the atmosphere when triggered.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[AutoGenerateComponentPause]
|
||||
[Access(typeof(SharedReleaseGasOnTriggerSystem))]
|
||||
public sealed partial class ReleaseGasOnTriggerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this grenade is active and releasing gas.
|
||||
/// Set to true when triggered, which starts gas release.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Active;
|
||||
|
||||
/// <summary>
|
||||
/// The gas mixture that will be released to the current tile atmosphere when triggered.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public GasMixture Air;
|
||||
|
||||
/// <summary>
|
||||
/// Time at which the next release will occur.
|
||||
/// This is automatically set when the grenade activates.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan NextReleaseTime = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The cap at which this grenade can fill the exposed atmosphere to.
|
||||
/// This component automatically removes itself when the pressure limit is reached.
|
||||
/// </summary>
|
||||
/// <example>If set to 101.325, the grenade will only fill the exposed
|
||||
/// atmosphere up to 101.325 kPa.</example>
|
||||
/// <remarks>If zero, this limit won't be respected.</remarks>
|
||||
[DataField]
|
||||
public float PressureLimit;
|
||||
|
||||
/// <summary>
|
||||
/// How often the grenade will release gas.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan ReleaseInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// A float from 0 to 1, representing a partial portion of the moles
|
||||
/// of the gas mixture that will be
|
||||
/// released to the current tile atmosphere when triggered.
|
||||
/// </summary>
|
||||
/// <remarks>If undefined on the prototype, the entire molar amount will be transferred.</remarks>
|
||||
[DataField]
|
||||
public float RemoveFraction = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the total moles initially in the grenade upon activation.
|
||||
/// Used to calculate the moles released over time.
|
||||
/// </summary>
|
||||
/// <remarks>Set when the grenade is activated.</remarks>
|
||||
[DataField(readOnly: true)]
|
||||
public float StartingTotalMoles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents visual states for whatever visuals that need to be applied
|
||||
/// on state changes.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public enum ReleaseGasOnTriggerVisuals : byte
|
||||
{
|
||||
Key,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared.Explosion.EntitySystems;
|
||||
|
||||
public abstract partial class SharedReleaseGasOnTriggerSystem : EntitySystem;
|
||||
|
||||
// I have dreams of Atmos in shared.
|
||||
@@ -36,14 +36,14 @@ public sealed partial class ExplosionPrototype : IPrototype
|
||||
/// explosion intensity to a tile break chance via linear interpolation.
|
||||
/// </summary>
|
||||
[DataField("tileBreakChance")]
|
||||
private float[] _tileBreakChance = { 0f, 1f };
|
||||
public float[] _tileBreakChance = { 0f, 1f };
|
||||
|
||||
/// <summary>
|
||||
/// This set of points, together with <see cref="_tileBreakChance"/> define a function that maps the
|
||||
/// explosion intensity to a tile break chance via linear interpolation.
|
||||
/// </summary>
|
||||
[DataField("tileBreakIntensity")]
|
||||
private float[] _tileBreakIntensity = {0f, 15f };
|
||||
public float[] _tileBreakIntensity = { 0f, 15f };
|
||||
|
||||
/// <summary>
|
||||
/// When a tile is broken by an explosion, the intensity is reduced by this amount and is used to try and
|
||||
@@ -115,19 +115,13 @@ public sealed partial class ExplosionPrototype : IPrototype
|
||||
/// </summary>
|
||||
public float TileBreakChance(float intensity)
|
||||
{
|
||||
if (_tileBreakChance.Length == 0 || _tileBreakChance.Length != _tileBreakIntensity.Length)
|
||||
{
|
||||
Logger.Error($"Malformed tile break chance definitions for explosion prototype: {ID}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (intensity >= _tileBreakIntensity[^1] || _tileBreakIntensity.Length == 1)
|
||||
return _tileBreakChance[^1];
|
||||
|
||||
if (intensity <= _tileBreakIntensity[0])
|
||||
return _tileBreakChance[0];
|
||||
|
||||
int i = Array.FindIndex(_tileBreakIntensity, k => k >= intensity);
|
||||
var i = Array.FindIndex(_tileBreakIntensity, k => k >= intensity);
|
||||
|
||||
var slope = (_tileBreakChance[i] - _tileBreakChance[i - 1]) / (_tileBreakIntensity[i] - _tileBreakIntensity[i - 1]);
|
||||
return _tileBreakChance[i - 1] + slope * (intensity - _tileBreakIntensity[i - 1]);
|
||||
|
||||
@@ -118,8 +118,11 @@ namespace Content.Shared.Friction
|
||||
// You may think you can just pass the body.LinearVelocity to the Friction function and edit it there!
|
||||
// But doing so is unpredicted! And you will doom yourself to 1000 years of rubber banding!
|
||||
var velocity = body.LinearVelocity;
|
||||
var angVelocity = body.AngularVelocity;
|
||||
_mover.Friction(0f, frameTime, friction, ref velocity);
|
||||
_mover.Friction(0f, frameTime, friction, ref angVelocity);
|
||||
PhysicsSystem.SetLinearVelocity(uid, velocity, body: body);
|
||||
PhysicsSystem.SetAngularVelocity(uid, angVelocity, body: body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
.Bind(ContentKeyFunctions.UseItemInHand, InputCmdHandler.FromDelegate(HandleUseItem, handle: false, outsidePrediction: false))
|
||||
.Bind(ContentKeyFunctions.AltUseItemInHand, InputCmdHandler.FromDelegate(HandleAltUseInHand, handle: false, outsidePrediction: false))
|
||||
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed, handle: false, outsidePrediction: false))
|
||||
.Bind(ContentKeyFunctions.SwapHandsReverse, InputCmdHandler.FromDelegate(SwapHandsReversePressed, handle: false, outsidePrediction: false))
|
||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||
.Register<SharedHandsSystem>();
|
||||
}
|
||||
@@ -79,6 +80,16 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
}
|
||||
|
||||
private void SwapHandsPressed(ICommonSession? session)
|
||||
{
|
||||
SwapHands(session, false);
|
||||
}
|
||||
|
||||
private void SwapHandsReversePressed(ICommonSession? session)
|
||||
{
|
||||
SwapHands(session, true);
|
||||
}
|
||||
|
||||
private void SwapHands(ICommonSession? session, bool reverse)
|
||||
{
|
||||
if (!TryComp(session?.AttachedEntity, out HandsComponent? component))
|
||||
return;
|
||||
@@ -89,8 +100,9 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
||||
if (component.ActiveHand == null || component.Hands.Count < 2)
|
||||
return;
|
||||
|
||||
var newActiveIndex = component.SortedHands.IndexOf(component.ActiveHand.Name) + 1;
|
||||
var nextHand = component.SortedHands[newActiveIndex % component.Hands.Count];
|
||||
var currentIndex = component.SortedHands.IndexOf(component.ActiveHand.Name);
|
||||
var newActiveIndex = (currentIndex + (reverse ? -1 : 1) + component.Hands.Count) % component.Hands.Count;
|
||||
var nextHand = component.SortedHands[newActiveIndex];
|
||||
|
||||
TrySetActiveHand(session.AttachedEntity.Value, nextHand, component);
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ public sealed partial class HolographicAvatarComponent : Component
|
||||
/// The prototype sprite layer data for the hologram
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public PrototypeLayerData[] LayerData;
|
||||
public PrototypeLayerData[]? LayerData = null;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Content.Shared.Implants;
|
||||
|
||||
public abstract class SharedSubdermalImplantSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
@@ -38,7 +37,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
||||
|
||||
private void OnInsert(EntityUid uid, SubdermalImplantComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (component.ImplantedEntity == null || _net.IsClient)
|
||||
if (component.ImplantedEntity == null)
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(component.ImplantAction))
|
||||
@@ -54,7 +53,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
||||
if (_tag.HasTag(implant, MicroBombTag))
|
||||
{
|
||||
_container.Remove(implant, implantContainer);
|
||||
QueueDel(implant);
|
||||
PredictedQueueDel(implant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction OpenBelt = "OpenBelt";
|
||||
public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
|
||||
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
||||
public static readonly BoundKeyFunction SwapHandsReverse = "SwapHandsReverse";
|
||||
public static readonly BoundKeyFunction MoveStoredItem = "MoveStoredItem";
|
||||
public static readonly BoundKeyFunction RotateStoredItem = "RotateStoredItem";
|
||||
public static readonly BoundKeyFunction SaveItemLocation = "SaveItemLocation";
|
||||
|
||||
@@ -34,6 +34,7 @@ using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
@@ -53,21 +54,22 @@ namespace Content.Shared.Interaction
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ISharedChatManager _chat = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
||||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _broadphase = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedVerbSystem _verbSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly SharedStrippableSystem _strippable = default!;
|
||||
[Dependency] private readonly SharedPlayerRateLimitManager _rateLimit = default!;
|
||||
[Dependency] private readonly ISharedChatManager _chat = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
|
||||
private EntityQuery<IgnoreUIRangeComponent> _ignoreUiRangeQuery;
|
||||
private EntityQuery<FixturesComponent> _fixtureQuery;
|
||||
@@ -854,7 +856,20 @@ namespace Content.Shared.Interaction
|
||||
{
|
||||
// If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck
|
||||
// inside of walls, users can still pick them up.
|
||||
ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics)); // Note: This also bypasses items underneath doors, which may be problematic if it'd cause undesirable behavior.
|
||||
// TODO: Bandaid, alloc spam
|
||||
// We use 0.01 range just in case it's perfectly in between 2 walls and 1 gets missed.
|
||||
foreach (var otherEnt in _lookup.GetEntitiesInRange(target, 0.01f, flags: LookupFlags.Static))
|
||||
{
|
||||
if (target == otherEnt ||
|
||||
!_physicsQuery.TryComp(otherEnt, out var otherBody) ||
|
||||
!otherBody.CanCollide ||
|
||||
((int) collisionMask & otherBody.CollisionLayer) == 0x0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ignored.Add(otherEnt);
|
||||
}
|
||||
}
|
||||
else if (_wallMountQuery.TryComp(target, out var wallMount))
|
||||
{
|
||||
|
||||
@@ -1,60 +1,55 @@
|
||||
namespace Content.Shared.Inventory.Events;
|
||||
|
||||
public abstract class EquipAttemptBase : CancellableEntityEventArgs
|
||||
public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
|
||||
|
||||
/// <summary>
|
||||
/// The entity performing the action. NOT necessarily the one actually "receiving" the equipment.
|
||||
/// </summary>
|
||||
public readonly EntityUid Equipee;
|
||||
public readonly EntityUid Equipee = equipee;
|
||||
|
||||
/// <summary>
|
||||
/// The entity being equipped to.
|
||||
/// </summary>
|
||||
public readonly EntityUid EquipTarget;
|
||||
public readonly EntityUid EquipTarget = equipTarget;
|
||||
|
||||
/// <summary>
|
||||
/// The entity to be equipped.
|
||||
/// </summary>
|
||||
public readonly EntityUid Equipment;
|
||||
public readonly EntityUid Equipment = equipment;
|
||||
|
||||
/// <summary>
|
||||
/// The slotFlags of the slot to equip the entity into.
|
||||
/// </summary>
|
||||
public readonly SlotFlags SlotFlags;
|
||||
public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags;
|
||||
|
||||
/// <summary>
|
||||
/// The slot the entity is being equipped to.
|
||||
/// </summary>
|
||||
public readonly string Slot;
|
||||
public readonly string Slot = slotDefinition.Name;
|
||||
|
||||
/// <summary>
|
||||
/// If cancelling and wanting to provide a custom reason, use this field. Not that this expects a loc-id.
|
||||
/// </summary>
|
||||
public string? Reason;
|
||||
|
||||
public EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition)
|
||||
{
|
||||
EquipTarget = equipTarget;
|
||||
Equipment = equipment;
|
||||
Equipee = equipee;
|
||||
SlotFlags = slotDefinition.SlotFlags;
|
||||
Slot = slotDefinition.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BeingEquippedAttemptEvent : EquipAttemptBase
|
||||
{
|
||||
public BeingEquippedAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : base(equipee, equipTarget, equipment, slotDefinition)
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised on the item that is being equipped.
|
||||
/// </summary>
|
||||
public sealed class BeingEquippedAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition);
|
||||
|
||||
public sealed class IsEquippingAttemptEvent : EquipAttemptBase
|
||||
{
|
||||
public IsEquippingAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : base(equipee, equipTarget, equipment, slotDefinition)
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised on the entity that is equipping an item.
|
||||
/// </summary>
|
||||
public sealed class IsEquippingAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the entity on who item is being equipped.
|
||||
/// </summary>
|
||||
public sealed class IsEquippingTargetAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition);
|
||||
|
||||
@@ -1,60 +1,55 @@
|
||||
namespace Content.Shared.Inventory.Events;
|
||||
|
||||
public abstract class UnequipAttemptEventBase : CancellableEntityEventArgs
|
||||
public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
|
||||
|
||||
/// <summary>
|
||||
/// The entity performing the action. NOT necessarily the same as the entity whose equipment is being removed..
|
||||
/// </summary>
|
||||
public readonly EntityUid Unequipee;
|
||||
public readonly EntityUid Unequipee = unequipee;
|
||||
|
||||
/// <summary>
|
||||
/// The entity being unequipped from.
|
||||
/// </summary>
|
||||
public readonly EntityUid UnEquipTarget;
|
||||
public readonly EntityUid UnEquipTarget = unEquipTarget;
|
||||
|
||||
/// <summary>
|
||||
/// The entity to be unequipped.
|
||||
/// </summary>
|
||||
public readonly EntityUid Equipment;
|
||||
public readonly EntityUid Equipment = equipment;
|
||||
|
||||
/// <summary>
|
||||
/// The slotFlags of the slot this item is being removed from.
|
||||
/// </summary>
|
||||
public readonly SlotFlags SlotFlags;
|
||||
public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags;
|
||||
|
||||
/// <summary>
|
||||
/// The slot the entity is being unequipped from.
|
||||
/// </summary>
|
||||
public readonly string Slot;
|
||||
public readonly string Slot = slotDefinition.Name;
|
||||
|
||||
/// <summary>
|
||||
/// If cancelling and wanting to provide a custom reason, use this field. Not that this expects a loc-id.
|
||||
/// </summary>
|
||||
public string? Reason;
|
||||
|
||||
public UnequipAttemptEventBase(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition)
|
||||
{
|
||||
UnEquipTarget = unEquipTarget;
|
||||
Equipment = equipment;
|
||||
Unequipee = unequipee;
|
||||
SlotFlags = slotDefinition.SlotFlags;
|
||||
Slot = slotDefinition.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BeingUnequippedAttemptEvent : UnequipAttemptEventBase
|
||||
{
|
||||
public BeingUnequippedAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : base(unequipee, unEquipTarget, equipment, slotDefinition)
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised on the item that is being unequipped.
|
||||
/// </summary>
|
||||
public sealed class BeingUnequippedAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition);
|
||||
|
||||
public sealed class IsUnequippingAttemptEvent : UnequipAttemptEventBase
|
||||
{
|
||||
public IsUnequippingAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : base(unequipee, unEquipTarget, equipment, slotDefinition)
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised on the entity that is unequipping an item.
|
||||
/// </summary>
|
||||
public sealed class IsUnequippingAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the entity from who item is being unequipped.
|
||||
/// </summary>
|
||||
public sealed class IsUnequippingTargetAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
|
||||
SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition);
|
||||
|
||||
@@ -286,23 +286,21 @@ public abstract partial class InventorySystem
|
||||
}
|
||||
|
||||
var attemptEvent = new IsEquippingAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
RaiseLocalEvent(target, attemptEvent, true);
|
||||
RaiseLocalEvent(actor, attemptEvent, true);
|
||||
|
||||
if (attemptEvent.Cancelled)
|
||||
{
|
||||
reason = attemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor != target)
|
||||
var targetAttemptEvent = new IsEquippingTargetAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
RaiseLocalEvent(target, targetAttemptEvent, true);
|
||||
|
||||
if (targetAttemptEvent.Cancelled)
|
||||
{
|
||||
//reuse the event. this is gucci, right?
|
||||
attemptEvent.Reason = null;
|
||||
RaiseLocalEvent(actor, attemptEvent, true);
|
||||
if (attemptEvent.Cancelled)
|
||||
{
|
||||
reason = attemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
reason = targetAttemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
var itemAttemptEvent = new BeingEquippedAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
@@ -524,23 +522,21 @@ public abstract partial class InventorySystem
|
||||
}
|
||||
|
||||
var attemptEvent = new IsUnequippingAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
RaiseLocalEvent(target, attemptEvent, true);
|
||||
RaiseLocalEvent(actor, attemptEvent, true);
|
||||
|
||||
if (attemptEvent.Cancelled)
|
||||
{
|
||||
reason = attemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor != target)
|
||||
var targetAttemptEvent = new IsUnequippingTargetAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
RaiseLocalEvent(target, targetAttemptEvent, true);
|
||||
|
||||
if (targetAttemptEvent.Cancelled)
|
||||
{
|
||||
//reuse the event. this is gucci, right?
|
||||
attemptEvent.Reason = null;
|
||||
RaiseLocalEvent(actor, attemptEvent, true);
|
||||
if (attemptEvent.Cancelled)
|
||||
{
|
||||
reason = attemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
reason = targetAttemptEvent.Reason ?? reason;
|
||||
return false;
|
||||
}
|
||||
|
||||
var itemAttemptEvent = new BeingUnequippedAttemptEvent(actor, target, itemUid, slotDefinition);
|
||||
|
||||
@@ -55,6 +55,8 @@ public partial class InventorySystem
|
||||
SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, CoefficientQueryEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ZombificationResistanceQueryEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, IsEquippingTargetAttemptEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, IsUnequippingTargetAttemptEvent>(RelayInventoryEvent);
|
||||
|
||||
// by-ref events
|
||||
SubscribeLocalEvent<InventoryComponent, RefreshFrictionModifiersEvent>(RefRelayInventoryEvent);
|
||||
|
||||
16
Content.Shared/Inventory/SlotBlockComponent.cs
Normal file
16
Content.Shared/Inventory/SlotBlockComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Used to prevent items from being unequipped and equipped from slots that are listed in <see cref="Slots"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SlotBlockSystem))]
|
||||
public sealed partial class SlotBlockComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Slots that this entity should block.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public SlotFlags Slots = SlotFlags.NONE;
|
||||
}
|
||||
35
Content.Shared/Inventory/SlotBlockSystem.cs
Normal file
35
Content.Shared/Inventory/SlotBlockSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
namespace Content.Shared.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Handles prevention of items being unequipped and equipped from slots that are blocked by <see cref="SlotBlockComponent"/>.
|
||||
/// </summary>
|
||||
public sealed partial class SlotBlockSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SlotBlockComponent, InventoryRelayedEvent<IsEquippingTargetAttemptEvent>>(OnEquipAttempt);
|
||||
SubscribeLocalEvent<SlotBlockComponent, InventoryRelayedEvent<IsUnequippingTargetAttemptEvent>>(OnUnequipAttempt);
|
||||
}
|
||||
|
||||
private void OnEquipAttempt(Entity<SlotBlockComponent> ent, ref InventoryRelayedEvent<IsEquippingTargetAttemptEvent> args)
|
||||
{
|
||||
if (args.Args.Cancelled || (args.Args.SlotFlags & ent.Comp.Slots) == 0)
|
||||
return;
|
||||
|
||||
args.Args.Reason = Loc.GetString("slot-block-component-blocked", ("item", ent));
|
||||
args.Args.Cancel();
|
||||
}
|
||||
|
||||
private void OnUnequipAttempt(Entity<SlotBlockComponent> ent, ref InventoryRelayedEvent<IsUnequippingTargetAttemptEvent> args)
|
||||
{
|
||||
if (args.Args.Cancelled || (args.Args.SlotFlags & ent.Comp.Slots) == 0)
|
||||
return;
|
||||
|
||||
args.Args.Reason = Loc.GetString("slot-block-component-blocked", ("item", ent));
|
||||
args.Args.Cancel();
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,26 @@ public sealed class ComponentTogglerSystem : EntitySystem
|
||||
|
||||
private void OnToggled(Entity<ComponentTogglerComponent> ent, ref ItemToggledEvent args)
|
||||
{
|
||||
var target = ent.Comp.Parent ? Transform(ent).ParentUid : ent.Owner;
|
||||
if (TerminatingOrDeleted(target))
|
||||
return;
|
||||
|
||||
if (args.Activated)
|
||||
{
|
||||
var target = ent.Comp.Parent ? Transform(ent).ParentUid : ent.Owner;
|
||||
|
||||
if (TerminatingOrDeleted(target))
|
||||
return;
|
||||
|
||||
ent.Comp.Target = target;
|
||||
|
||||
EntityManager.AddComponents(target, ent.Comp.Components);
|
||||
}
|
||||
else
|
||||
EntityManager.RemoveComponents(target, ent.Comp.RemoveComponents ?? ent.Comp.Components);
|
||||
{
|
||||
if (ent.Comp.Target == null)
|
||||
return;
|
||||
|
||||
if (TerminatingOrDeleted(ent.Comp.Target.Value))
|
||||
return;
|
||||
|
||||
EntityManager.RemoveComponents(ent.Comp.Target.Value, ent.Comp.RemoveComponents ?? ent.Comp.Components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,10 @@ public sealed partial class ComponentTogglerComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Parent;
|
||||
|
||||
// <summary>
|
||||
// It holds the entity that the component gave the component to, so it can remove from it even if it changes parent.
|
||||
// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Target;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Examine;
|
||||
using Content.Shared.Item.ItemToggle.Components;
|
||||
using Content.Shared.Storage;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -206,15 +207,21 @@ public abstract class SharedItemSystem : EntitySystem
|
||||
public IReadOnlyList<Box2i> GetAdjustedItemShape(Entity<ItemComponent?> entity, Angle rotation, Vector2i position)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return new Box2i[] { };
|
||||
return [];
|
||||
|
||||
var adjustedShapes = new List<Box2i>();
|
||||
GetAdjustedItemShape(adjustedShapes, entity, rotation, position);
|
||||
return adjustedShapes;
|
||||
}
|
||||
|
||||
public void GetAdjustedItemShape(List<Box2i> adjustedShapes, Entity<ItemComponent?> entity, Angle rotation, Vector2i position)
|
||||
{
|
||||
var shapes = GetItemShape(entity);
|
||||
var boundingShape = shapes.GetBoundingBox();
|
||||
var boundingCenter = ((Box2) boundingShape).Center;
|
||||
var matty = Matrix3Helpers.CreateTransform(boundingCenter, rotation);
|
||||
var drift = boundingShape.BottomLeft - matty.TransformBox(boundingShape).BottomLeft;
|
||||
|
||||
var adjustedShapes = new List<Box2i>();
|
||||
foreach (var shape in shapes)
|
||||
{
|
||||
var transformed = matty.TransformBox(shape).Translated(drift);
|
||||
@@ -223,8 +230,6 @@ public abstract class SharedItemSystem : EntitySystem
|
||||
|
||||
adjustedShapes.Add(translated);
|
||||
}
|
||||
|
||||
return adjustedShapes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Labels.Components;
|
||||
@@ -150,6 +151,25 @@ public sealed partial class LabelSystem : EntitySystem
|
||||
if (TryComp<PaperLabelTypeComponent>(slot.Item, out var type))
|
||||
_appearance.SetData(ent, PaperLabelVisuals.LabelType, type.PaperType, ent.Comp2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a label with the specified component from the default label slot.
|
||||
/// </summary>
|
||||
public bool TryGetLabel<T>(Entity<PaperLabelComponent?> ent, [NotNullWhen(true)] out Entity<T>? label) where T : Component
|
||||
{
|
||||
label = null;
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return false;
|
||||
|
||||
if (ent.Comp.LabelSlot.Item is not { } labelEnt)
|
||||
return false;
|
||||
|
||||
if (!TryComp<T>(labelEnt, out var labelComp))
|
||||
return false;
|
||||
|
||||
label = (labelEnt, labelComp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//CP14 Labeling Event
|
||||
|
||||
13
Content.Shared/Light/Components/ImplicitRoofComponent.cs
Normal file
13
Content.Shared/Light/Components/ImplicitRoofComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Light.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Assumes the entire attached grid is rooved.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class ImplicitRoofComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public Color Color = Color.Black;
|
||||
}
|
||||
@@ -43,7 +43,6 @@ namespace Content.Shared.Magic;
|
||||
public abstract class SharedMagicSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISerializationManager _seriMan = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
@@ -276,13 +275,9 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
|
||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
||||
var fromMap = _transform.ToMapCoordinates(fromCoords);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(fromMap, out var gridUid, out _)
|
||||
? _transform.WithEntityId(fromCoords, gridUid)
|
||||
: new(_mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position);
|
||||
|
||||
var ent = Spawn(ev.Prototype, spawnCoords);
|
||||
var ent = Spawn(ev.Prototype, fromMap);
|
||||
var direction = _transform.ToMapCoordinates(toCoords).Position -
|
||||
_transform.ToMapCoordinates(spawnCoords).Position;
|
||||
fromMap.Position;
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer, ev.Performer);
|
||||
}
|
||||
// End Projectile Spells
|
||||
@@ -360,7 +355,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
if (HasComp(target, data.Component.GetType()))
|
||||
continue;
|
||||
|
||||
var component = (Component)_compFact.GetComponent(name);
|
||||
var component = (Component)Factory.GetComponent(name);
|
||||
var temp = (object)component;
|
||||
_seriMan.CopyTo(data.Component, ref temp);
|
||||
EntityManager.AddComponent(target, (Component)temp!);
|
||||
@@ -371,7 +366,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
{
|
||||
foreach (var toRemove in comps)
|
||||
{
|
||||
if (_compFact.TryGetRegistration(toRemove, out var registration))
|
||||
if (Factory.TryGetRegistration(toRemove, out var registration))
|
||||
RemComp(target, registration.Type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ namespace Content.Shared.Maps
|
||||
[DataField]
|
||||
public PrototypeFlags<ToolQualityPrototype> DeconstructTools { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Effective mass of this tile for grid impacts.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Mass = 800f;
|
||||
|
||||
/// <remarks>
|
||||
/// Legacy AF but nice to have.
|
||||
/// </remarks>
|
||||
@@ -69,6 +75,11 @@ namespace Content.Shared.Maps
|
||||
|
||||
[DataField("variants")] public byte Variants { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the tile to be rotated/mirrored when placed on a grid.
|
||||
/// </summary>
|
||||
[DataField] public bool AllowRotationMirror { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// This controls what variants the `variantize` command is allowed to use.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,6 +10,7 @@ public struct TileFrictionEvent
|
||||
|
||||
public TileFrictionEvent(float modifier)
|
||||
{
|
||||
// TODO: If something ever uses different angular and linear modifiers, split this into two modifiers
|
||||
Modifier = modifier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
@@ -28,6 +30,7 @@ using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Movement.Pulling.Systems;
|
||||
|
||||
@@ -48,6 +51,7 @@ public sealed class PullingSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly HeldSpeedModifierSystem _clothingMoveSpeed = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedVirtualItemSystem _virtual = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -73,6 +77,9 @@ public sealed class PullingSystem : EntitySystem
|
||||
SubscribeLocalEvent<PullerComponent, DropHandItemsEvent>(OnDropHandItems);
|
||||
SubscribeLocalEvent<PullerComponent, StopPullingAlertEvent>(OnStopPullingAlert);
|
||||
|
||||
SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
|
||||
SubscribeLocalEvent<HandsComponent, PullStoppedMessage>(HandlePullStopped);
|
||||
|
||||
SubscribeLocalEvent<PullableComponent, StrappedEvent>(OnBuckled);
|
||||
SubscribeLocalEvent<PullableComponent, BuckledEvent>(OnGotBuckled);
|
||||
|
||||
@@ -81,6 +88,41 @@ public sealed class PullingSystem : EntitySystem
|
||||
.Register<PullingSystem>();
|
||||
}
|
||||
|
||||
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
|
||||
{
|
||||
if (args.PullerUid != uid)
|
||||
return;
|
||||
|
||||
if (TryComp(args.PullerUid, out PullerComponent? pullerComp) && !pullerComp.NeedsHands)
|
||||
return;
|
||||
|
||||
if (!_virtual.TrySpawnVirtualItemInHand(args.PulledUid, uid))
|
||||
{
|
||||
DebugTools.Assert("Unable to find available hand when starting pulling??");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
|
||||
{
|
||||
if (args.PullerUid != uid)
|
||||
return;
|
||||
|
||||
// Try find hand that is doing this pull.
|
||||
// and clear it.
|
||||
foreach (var hand in component.Hands.Values)
|
||||
{
|
||||
if (hand.HeldEntity == null
|
||||
|| !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
|
||||
|| virtualItem.BlockingEntity != args.PulledUid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_handsSystem.TryDrop(args.PullerUid, hand, handsComp: component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStateChanged(EntityUid uid, PullerComponent component, ref UpdateMobStateEvent args)
|
||||
{
|
||||
if (component.Pulling == null)
|
||||
|
||||
@@ -210,12 +210,12 @@ namespace Content.Shared.Movement.Systems
|
||||
var diff = relativeRot - oldRelativeRot;
|
||||
|
||||
// If we're going from a grid -> map then preserve the relative rotation so it's seamless if they go into space and back.
|
||||
if (HasComp<MapComponent>(relative) && HasComp<MapGridComponent>(mover.RelativeEntity))
|
||||
if (MapQuery.HasComp(relative) && MapGridQuery.HasComp(mover.RelativeEntity))
|
||||
{
|
||||
mover.TargetRelativeRotation -= diff;
|
||||
}
|
||||
// Snap to nearest cardinal if map -> grid
|
||||
else if (HasComp<MapGridComponent>(relative) && HasComp<MapComponent>(mover.RelativeEntity))
|
||||
// Snap to nearest cardinal if map -> grid or grid -> grid
|
||||
else if (MapGridQuery.HasComp(relative) && (MapQuery.HasComp(mover.RelativeEntity) || MapGridQuery.HasComp(mover.RelativeEntity)))
|
||||
{
|
||||
var targetDir = mover.TargetRelativeRotation - diff;
|
||||
targetDir = targetDir.GetCardinalDir().ToAngle().Reduced();
|
||||
|
||||
@@ -50,18 +50,19 @@ public abstract partial class SharedMoverController : VirtualController
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly TagSystem _tags = default!;
|
||||
|
||||
protected EntityQuery<CanMoveInAirComponent> CanMoveInAirQuery;
|
||||
protected EntityQuery<FootstepModifierComponent> FootstepModifierQuery;
|
||||
protected EntityQuery<InputMoverComponent> MoverQuery;
|
||||
protected EntityQuery<MapComponent> MapQuery;
|
||||
protected EntityQuery<MapGridComponent> MapGridQuery;
|
||||
protected EntityQuery<MobMoverComponent> MobMoverQuery;
|
||||
protected EntityQuery<MovementRelayTargetComponent> RelayTargetQuery;
|
||||
protected EntityQuery<MovementSpeedModifierComponent> ModifierQuery;
|
||||
protected EntityQuery<NoRotateOnMoveComponent> NoRotateQuery;
|
||||
protected EntityQuery<PhysicsComponent> PhysicsQuery;
|
||||
protected EntityQuery<RelayInputMoverComponent> RelayQuery;
|
||||
protected EntityQuery<PullableComponent> PullableQuery;
|
||||
protected EntityQuery<TransformComponent> XformQuery;
|
||||
protected EntityQuery<CanMoveInAirComponent> CanMoveInAirQuery;
|
||||
protected EntityQuery<NoRotateOnMoveComponent> NoRotateQuery;
|
||||
protected EntityQuery<FootstepModifierComponent> FootstepModifierQuery;
|
||||
protected EntityQuery<MapGridComponent> MapGridQuery;
|
||||
|
||||
private static readonly ProtoId<TagPrototype> FootstepSoundTag = "FootstepSound";
|
||||
|
||||
@@ -91,6 +92,7 @@ public abstract partial class SharedMoverController : VirtualController
|
||||
CanMoveInAirQuery = GetEntityQuery<CanMoveInAirComponent>();
|
||||
FootstepModifierQuery = GetEntityQuery<FootstepModifierComponent>();
|
||||
MapGridQuery = GetEntityQuery<MapGridComponent>();
|
||||
MapQuery = GetEntityQuery<MapComponent>();
|
||||
|
||||
SubscribeLocalEvent<MovementSpeedModifierComponent, TileFrictionEvent>(OnTileFriction);
|
||||
|
||||
@@ -412,6 +414,17 @@ public abstract partial class SharedMoverController : VirtualController
|
||||
|
||||
}
|
||||
|
||||
public void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref float velocity)
|
||||
{
|
||||
if (velocity < minimumFrictionSpeed)
|
||||
return;
|
||||
|
||||
// This equation is lifted from the Physics Island solver.
|
||||
// We re-use it here because Kinematic Controllers can't/shouldn't use the Physics Friction
|
||||
velocity *= Math.Clamp(1.0f - frameTime * friction, 0.0f, 1.0f);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the current velocity to the target velocity based on the specified acceleration.
|
||||
/// </summary>
|
||||
|
||||
@@ -113,7 +113,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem
|
||||
var evSlippery = new GetSlowedOverSlipperyModifierEvent();
|
||||
RaiseLocalEvent(uid, ref evSlippery);
|
||||
|
||||
if (MathHelper.CloseTo(evSlippery.SlowdownModifier, 1))
|
||||
if (!MathHelper.CloseTo(evSlippery.SlowdownModifier, 1))
|
||||
{
|
||||
walkSpeed += evSlippery.SlowdownModifier;
|
||||
sprintSpeed += evSlippery.SlowdownModifier;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Shared.NodeContainer;
|
||||
|
||||
public abstract class SharedNodeContainerSystem : EntitySystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -8,5 +8,16 @@ namespace Content.Shared.Nuke;
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class NukeDiskComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to modify the nuke's countdown timer.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan? TimeModifier;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan MicrowaveMean = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan MicrowaveStd = TimeSpan.FromSeconds(27.35);
|
||||
// STD of 27.36s means theres an 90% chance the time is between +-45s, and a ~99% chance its between +-70s
|
||||
}
|
||||
|
||||
@@ -25,10 +25,8 @@ public abstract class SharedRingerSystem : EntitySystem
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedPdaSystem _pda = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
[Dependency] protected readonly SharedUserInterfaceSystem UI = default!;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public sealed partial class PaperComponent : Component
|
||||
public string Content { get; set; } = "";
|
||||
|
||||
[DataField("contentSize")]
|
||||
public int ContentSize { get; set; } = 6000;
|
||||
public int ContentSize { get; set; } = 10000;
|
||||
|
||||
[DataField("stampedBy"), AutoNetworkedField]
|
||||
public List<StampDisplayInfo> StampedBy { get; set; } = new();
|
||||
|
||||
@@ -122,7 +122,7 @@ public sealed class PaperSystem : EntitySystem
|
||||
if (entity.Comp.EditingDisabled)
|
||||
{
|
||||
var paperEditingDisabledMessage = Loc.GetString("paper-tamper-proof-modified-message");
|
||||
_popupSystem.PopupEntity(paperEditingDisabledMessage, entity, args.User);
|
||||
_popupSystem.PopupClient(paperEditingDisabledMessage, entity, args.User);
|
||||
|
||||
args.Handled = true;
|
||||
return;
|
||||
@@ -281,6 +281,12 @@ public sealed class PaperSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public void SetContent(EntityUid entity, string content)
|
||||
{
|
||||
if (!TryComp<PaperComponent>(entity, out var paper))
|
||||
return;
|
||||
SetContent((entity, paper), content);
|
||||
}
|
||||
|
||||
public void SetContent(Entity<PaperComponent> entity, string content)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
[Dependency] private readonly ISerializationManager _serManager = default!;
|
||||
[Dependency] protected readonly ITileDefinitionManager TileDefManager = default!;
|
||||
[Dependency] private readonly TileSystem _tile = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
protected const byte ChunkSize = 8;
|
||||
|
||||
@@ -69,7 +70,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
|
||||
public bool TryGetBiomeTile(EntityUid uid, MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
if (grid.TryGetTileRef(indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||
if (_map.TryGetTileRef(uid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||
{
|
||||
tile = tileRef.Tile;
|
||||
return true;
|
||||
@@ -81,15 +82,15 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetBiomeTile(indices, biome.Layers, biome.Seed, grid, out tile);
|
||||
return TryGetBiomeTile(indices, biome.Layers, biome.Seed, (uid, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the tile, real or otherwise, for the specified indices.
|
||||
/// </summary>
|
||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
if (grid?.TryGetTileRef(indices, out var tileRef) == true && !tileRef.Tile.IsEmpty)
|
||||
if (grid is { } gridEnt && _map.TryGetTileRef(gridEnt, gridEnt.Comp, indices, out var tileRef) && !tileRef.Tile.IsEmpty)
|
||||
{
|
||||
tile = tileRef.Tile;
|
||||
return true;
|
||||
@@ -98,10 +99,19 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return TryGetTile(indices, layers, seed, grid, out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the tile, real or otherwise, for the specified indices.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetBiomeTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
return TryGetBiomeTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
public bool TryGetTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
public bool TryGetTile(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
for (var i = layers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -140,6 +150,15 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetTile(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile)
|
||||
{
|
||||
return TryGetTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
|
||||
/// </summary>
|
||||
@@ -161,7 +180,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
if (variantCount > 1)
|
||||
{
|
||||
var variantValue = (noise.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) * 100;
|
||||
variant = _tile.PickVariant(tileDef, (int) variantValue);
|
||||
variant = _tile.PickVariant(tileDef, (int)variantValue);
|
||||
}
|
||||
|
||||
tile = new Tile(tileDef.TileId, variant);
|
||||
@@ -171,7 +190,7 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Tries to get the relevant entity for this tile.
|
||||
/// </summary>
|
||||
public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid,
|
||||
public bool TryGetEntity(Vector2i indices, BiomeComponent component, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
if (!TryGetBiomeTile(indices, component.Layers, component.Seed, grid, out var tile))
|
||||
@@ -183,8 +202,17 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return TryGetEntity(indices, component.Layers, tile.Value, component.Seed, grid, out entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant entity for this tile.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
return TryGetEntity(indices, component, grid == null ? null : (grid.Owner, grid), out entity);
|
||||
}
|
||||
|
||||
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
|
||||
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
var tileId = TileDefManager[tileRef.TypeId].ID;
|
||||
@@ -243,10 +271,17 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetEntity(Vector2i indices, List<IBiomeLayer> layers, Tile tileRef, int seed, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out string? entity)
|
||||
{
|
||||
return TryGetEntity(indices, layers, tileRef, seed, grid == null ? null : (grid.Owner, grid), out entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant decals for this tile.
|
||||
/// </summary>
|
||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent grid,
|
||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, Entity<MapGridComponent>? grid,
|
||||
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
||||
{
|
||||
if (!TryGetBiomeTile(indices, layers, seed, grid, out var tileRef))
|
||||
@@ -330,6 +365,16 @@ public abstract class SharedBiomeSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the relevant decals for this tile.
|
||||
/// </summary>
|
||||
[Obsolete("Use the Entity<MapGridComponent>? overload")]
|
||||
public bool TryGetDecals(Vector2i indices, List<IBiomeLayer> layers, int seed, MapGridComponent grid,
|
||||
[NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals)
|
||||
{
|
||||
return TryGetDecals(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out decals);
|
||||
}
|
||||
|
||||
private FastNoiseLite GetNoise(FastNoiseLite seedNoise, int seed)
|
||||
{
|
||||
var noiseCopy = new FastNoiseLite();
|
||||
|
||||
@@ -12,17 +12,4 @@ public sealed partial class ParallaxComponent : Component
|
||||
// I wish I could use a typeserializer here but parallax is extremely client-dependent.
|
||||
[DataField, AutoNetworkedField]
|
||||
public string Parallax = "Default";
|
||||
|
||||
[UsedImplicitly, ViewVariables(VVAccess.ReadWrite)]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public string ParallaxVV
|
||||
{
|
||||
get => Parallax;
|
||||
set
|
||||
{
|
||||
if (value.Equals(Parallax)) return;
|
||||
Parallax = value;
|
||||
IoCManager.Resolve<IEntityManager>().Dirty(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@ public enum CollisionGroup
|
||||
WallLayer = Opaque | Impassable | HighImpassable | MidImpassable | LowImpassable | BulletImpassable | InteractImpassable,
|
||||
GlassLayer = Impassable | HighImpassable | MidImpassable | LowImpassable | BulletImpassable | InteractImpassable,
|
||||
HalfWallLayer = MidImpassable | LowImpassable,
|
||||
// Allows people to interact past and target players inside of this
|
||||
SpecialWallLayer = Opaque | HighImpassable | MidImpassable | LowImpassable | BulletImpassable,
|
||||
|
||||
// Statue, monument, airlock, window
|
||||
FullTileMask = Impassable | HighImpassable | MidImpassable | LowImpassable | InteractImpassable,
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
using Content.Shared.Power.Components;
|
||||
|
||||
namespace Content.Shared.Power.EntitySystems;
|
||||
|
||||
public abstract class SharedPowerNetSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public abstract bool IsPoweredCalculate(SharedApcPowerReceiverComponent comp);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AppearanceComponent, PowerChangedEvent>(OnPowerAppearance);
|
||||
}
|
||||
|
||||
private void OnPowerAppearance(Entity<AppearanceComponent> ent, ref PowerChangedEvent args)
|
||||
{
|
||||
_appearance.SetData(ent, PowerDeviceVisuals.Powered, args.Powered, ent.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Power.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared.Power.EntitySystems;
|
||||
|
||||
public abstract class SharedPowerReceiverSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPowerNetSystem _net = default!;
|
||||
|
||||
public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component);
|
||||
|
||||
@@ -44,6 +47,15 @@ public abstract class SharedPowerReceiverSystem : EntitySystem
|
||||
// it'll save a lot of confusion if 'always powered' means 'always powered'
|
||||
if (!receiver.NeedsPower)
|
||||
{
|
||||
var powered = _net.IsPoweredCalculate(receiver);
|
||||
|
||||
// Server won't raise it here as it can raise the load event later with NeedsPower?
|
||||
// This is mostly here for clientside predictions.
|
||||
if (receiver.Powered != powered)
|
||||
{
|
||||
RaisePower((uid, receiver));
|
||||
}
|
||||
|
||||
SetPowerDisabled(uid, false, receiver);
|
||||
return true;
|
||||
}
|
||||
@@ -59,6 +71,19 @@ public abstract class SharedPowerReceiverSystem : EntitySystem
|
||||
AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
if (_netMan.IsClient && receiver.PowerDisabled)
|
||||
{
|
||||
var powered = _net.IsPoweredCalculate(receiver);
|
||||
|
||||
// Server won't raise it here as it can raise the load event later with NeedsPower?
|
||||
// This is mostly here for clientside predictions.
|
||||
if (receiver.Powered != powered)
|
||||
{
|
||||
receiver.Powered = powered;
|
||||
RaisePower((uid, receiver));
|
||||
}
|
||||
}
|
||||
|
||||
return !receiver.PowerDisabled; // i.e. PowerEnabled
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
||||
|
||||
private void OnMapInit(Entity<PowerCellDrawComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
QueueUpdate((ent, ent.Comp));
|
||||
ent.Comp.NextUpdateTime = Timing.CurTime + ent.Comp.Delay;
|
||||
}
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args)
|
||||
@@ -71,20 +71,14 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
||||
RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the draw logic update in the next tick.
|
||||
/// </summary>
|
||||
public void QueueUpdate(Entity<PowerCellDrawComponent?> ent)
|
||||
{
|
||||
if (Resolve(ent, ref ent.Comp))
|
||||
ent.Comp.NextUpdateTime = Timing.CurTime;
|
||||
}
|
||||
|
||||
public void SetDrawEnabled(Entity<PowerCellDrawComponent?> ent, bool enabled)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
ent.Comp.NextUpdateTime = Timing.CurTime;
|
||||
|
||||
ent.Comp.Enabled = enabled;
|
||||
Dirty(ent, ent.Comp);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ public sealed class ToggleCellDrawSystem : EntitySystem
|
||||
{
|
||||
var uid = ent.Owner;
|
||||
var draw = Comp<PowerCellDrawComponent>(uid);
|
||||
_cell.QueueUpdate((uid, draw));
|
||||
_cell.SetDrawEnabled((uid, draw), args.Activated);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,6 @@ namespace Content.Shared.Preferences
|
||||
private static readonly Regex RestrictedNameRegex = new("[^А-Я,а-я,A-Z,a-z,0-9, ,\\-,']"); //CP14 Cyrillic add
|
||||
private static readonly Regex ICNameCaseRegex = new(@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)");
|
||||
|
||||
public const int MaxNameLength = 32;
|
||||
public const int MaxLoadoutNameLength = 32;
|
||||
public const int MaxDescLength = 512;
|
||||
|
||||
/// <summary>
|
||||
/// Job preferences for initial spawn.
|
||||
/// </summary>
|
||||
@@ -508,13 +504,14 @@ namespace Content.Shared.Preferences
|
||||
};
|
||||
|
||||
string name;
|
||||
var maxNameLength = configManager.GetCVar(CCVars.MaxNameLength);
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
{
|
||||
name = GetName(Species, gender);
|
||||
}
|
||||
else if (Name.Length > MaxNameLength)
|
||||
else if (Name.Length > maxNameLength)
|
||||
{
|
||||
name = Name[..MaxNameLength];
|
||||
name = Name[..maxNameLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -540,9 +537,10 @@ namespace Content.Shared.Preferences
|
||||
}
|
||||
|
||||
string flavortext;
|
||||
if (FlavorText.Length > MaxDescLength)
|
||||
var maxFlavorTextLength = configManager.GetCVar(CCVars.MaxFlavorTextLength);
|
||||
if (FlavorText.Length > maxFlavorTextLength)
|
||||
{
|
||||
flavortext = FormattedMessage.RemoveMarkupOrThrow(FlavorText)[..MaxDescLength];
|
||||
flavortext = FormattedMessage.RemoveMarkupOrThrow(FlavorText)[..maxFlavorTextLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -59,6 +61,7 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
|
||||
{
|
||||
var groupRemove = new ValueList<string>();
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
var configManager = collection.Resolve<IConfigurationManager>();
|
||||
|
||||
if (!protoManager.TryIndex(Role, out var roleProto))
|
||||
{
|
||||
@@ -78,10 +81,11 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
|
||||
if (EntityName != null)
|
||||
{
|
||||
var name = EntityName.Trim();
|
||||
var maxNameLength = configManager.GetCVar(CCVars.MaxNameLength);
|
||||
|
||||
if (name.Length > HumanoidCharacterProfile.MaxNameLength)
|
||||
if (name.Length > maxNameLength)
|
||||
{
|
||||
EntityName = name[..HumanoidCharacterProfile.MaxNameLength];
|
||||
EntityName = name[..maxNameLength];
|
||||
}
|
||||
|
||||
if (name.Length == 0)
|
||||
|
||||
36
Content.Shared/Preferences/MsgUpdateConstructionFavorites.cs
Normal file
36
Content.Shared/Preferences/MsgUpdateConstructionFavorites.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Preferences;
|
||||
|
||||
/// <summary>
|
||||
/// The client sends this to update their construction favorites.
|
||||
/// </summary>
|
||||
public sealed class MsgUpdateConstructionFavorites : NetMessage
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public List<ProtoId<ConstructionPrototype>> Favorites = [];
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||
{
|
||||
var length = buffer.ReadVariableInt32();
|
||||
Favorites.Clear();
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
Favorites.Add(new ProtoId<ConstructionPrototype>(buffer.ReadString()));
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||
{
|
||||
buffer.WriteVariableInt32(Favorites.Count);
|
||||
foreach (var favorite in Favorites)
|
||||
{
|
||||
buffer.Write(favorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -13,11 +15,12 @@ namespace Content.Shared.Preferences
|
||||
{
|
||||
private Dictionary<int, ICharacterProfile> _characters;
|
||||
|
||||
public PlayerPreferences(IEnumerable<KeyValuePair<int, ICharacterProfile>> characters, int selectedCharacterIndex, Color adminOOCColor)
|
||||
public PlayerPreferences(IEnumerable<KeyValuePair<int, ICharacterProfile>> characters, int selectedCharacterIndex, Color adminOOCColor, List<ProtoId<ConstructionPrototype>> constructionFavorites)
|
||||
{
|
||||
_characters = new Dictionary<int, ICharacterProfile>(characters);
|
||||
SelectedCharacterIndex = selectedCharacterIndex;
|
||||
AdminOOCColor = adminOOCColor;
|
||||
ConstructionFavorites = constructionFavorites;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +45,11 @@ namespace Content.Shared.Preferences
|
||||
|
||||
public Color AdminOOCColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of favorite items in the construction menu.
|
||||
/// </summary>
|
||||
public List<ProtoId<ConstructionPrototype>> ConstructionFavorites { get; set; } = [];
|
||||
|
||||
public int IndexOfCharacter(ICharacterProfile profile)
|
||||
{
|
||||
return _characters.FirstOrNull(p => p.Value == profile)?.Key ?? -1;
|
||||
|
||||
@@ -5,12 +5,6 @@ namespace Content.Shared.Procedural;
|
||||
[Virtual, DataDefinition]
|
||||
public partial class DungeonConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Data"/>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public DungeonData Data = DungeonData.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The secret sauce, procedural generation layers that get run.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Procedural;
|
||||
|
||||
/// <summary>
|
||||
/// Used to set dungeon values for all layers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This lets us share data between different dungeon configs without having to repeat entire configs.
|
||||
/// </remarks>
|
||||
[DataRecord]
|
||||
public sealed partial class DungeonData
|
||||
{
|
||||
// I hate this but it also significantly reduces yaml bloat if we add like 10 variations on the same set of layers
|
||||
// e.g. science rooms, engi rooms, cargo rooms all under PlanetBase for example.
|
||||
// without having to do weird nesting. It also means we don't need to copy-paste the same prototype across several layers
|
||||
// The alternative is doing like,
|
||||
// 2 layer prototype, 1 layer with the specified data, 3 layer prototype, 2 layers with specified data, etc.
|
||||
// As long as we just keep the code clean over time it won't be bad to maintain.
|
||||
|
||||
public static DungeonData Empty = new();
|
||||
|
||||
public Dictionary<DungeonDataKey, Color> Colors = new();
|
||||
public Dictionary<DungeonDataKey, EntProtoId> Entities = new();
|
||||
public Dictionary<DungeonDataKey, ProtoId<EntitySpawnEntryPrototype>> SpawnGroups = new();
|
||||
public Dictionary<DungeonDataKey, ProtoId<ContentTileDefinition>> Tiles = new();
|
||||
public Dictionary<DungeonDataKey, EntityWhitelist> Whitelists = new();
|
||||
|
||||
/// <summary>
|
||||
/// Applies the specified data to this data.
|
||||
/// </summary>
|
||||
public void Apply(DungeonData data)
|
||||
{
|
||||
// Copy-paste moment.
|
||||
foreach (var color in data.Colors)
|
||||
{
|
||||
Colors[color.Key] = color.Value;
|
||||
}
|
||||
|
||||
foreach (var color in data.Entities)
|
||||
{
|
||||
Entities[color.Key] = color.Value;
|
||||
}
|
||||
|
||||
foreach (var color in data.SpawnGroups)
|
||||
{
|
||||
SpawnGroups[color.Key] = color.Value;
|
||||
}
|
||||
|
||||
foreach (var color in data.Tiles)
|
||||
{
|
||||
Tiles[color.Key] = color.Value;
|
||||
}
|
||||
|
||||
foreach (var color in data.Whitelists)
|
||||
{
|
||||
Whitelists[color.Key] = color.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public DungeonData Clone()
|
||||
{
|
||||
return new DungeonData
|
||||
{
|
||||
// Only shallow clones but won't matter for DungeonJob purposes.
|
||||
Colors = Colors.ShallowClone(),
|
||||
Entities = Entities.ShallowClone(),
|
||||
SpawnGroups = SpawnGroups.ShallowClone(),
|
||||
Tiles = Tiles.ShallowClone(),
|
||||
Whitelists = Whitelists.ShallowClone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum DungeonDataKey : byte
|
||||
{
|
||||
// Colors
|
||||
Decals,
|
||||
|
||||
// Entities
|
||||
Cabling,
|
||||
CornerWalls,
|
||||
Fill,
|
||||
Junction,
|
||||
Walls,
|
||||
|
||||
// SpawnGroups
|
||||
CornerClutter,
|
||||
Entrance,
|
||||
EntranceFlank,
|
||||
WallMounts,
|
||||
Window,
|
||||
|
||||
// Tiles
|
||||
FallbackTile,
|
||||
WidenTile,
|
||||
|
||||
// Whitelists
|
||||
Rooms,
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Procedural.DungeonGenerators;
|
||||
@@ -6,10 +8,6 @@ namespace Content.Shared.Procedural.DungeonGenerators;
|
||||
/// Places rooms in pre-selected pack layouts. Chooses rooms from the specified whitelist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// DungeonData keys are:
|
||||
/// - FallbackTile
|
||||
/// - Rooms
|
||||
/// </remarks>
|
||||
public sealed partial class PrefabDunGen : IDunGenLayer
|
||||
{
|
||||
/// <summary>
|
||||
@@ -17,4 +15,10 @@ public sealed partial class PrefabDunGen : IDunGenLayer
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<DungeonPresetPrototype>> Presets = new();
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? RoomWhitelist;
|
||||
|
||||
[DataField]
|
||||
public ProtoId<ContentTileDefinition>? FallbackTile;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,30 @@ namespace Content.Shared.Procedural.DungeonGenerators;
|
||||
/// </summary>
|
||||
public sealed partial class PrototypeDunGen : IDunGenLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Should we pass in the current level's dungeons to the prototype.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public DungeonInheritance InheritDungeons = DungeonInheritance.None;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DungeonConfigPrototype> Proto;
|
||||
}
|
||||
|
||||
public enum DungeonInheritance : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't inherit any of the current layer's dungeons for this <see cref="PrototypeDunGen"/>
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Inherit only the last dungeon ran.
|
||||
/// </summary>
|
||||
Last,
|
||||
|
||||
/// <summary>
|
||||
/// Inherit all of the current layer's dungeons.
|
||||
/// </summary>
|
||||
All,
|
||||
}
|
||||
|
||||
@@ -18,4 +18,10 @@ public sealed partial class EntityTableDunGen : IDunGenLayer
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntityTableSelector Table;
|
||||
|
||||
/// <summary>
|
||||
/// Should the count be per dungeon or across all dungeons.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool PerDungeon;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Procedural.DungeonGenerators;
|
||||
namespace Content.Shared.Procedural.DungeonLayers;
|
||||
|
||||
/// <summary>
|
||||
/// Fills unreserved tiles with the specified entity prototype.
|
||||
@@ -17,4 +17,7 @@ public sealed partial class FillGridDunGen : IDunGenLayer
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<ProtoId<ContentTileDefinition>>? AllowedTiles;
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Entity;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Content.Shared.EntityTable;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Procedural.DungeonLayers;
|
||||
|
||||
@@ -17,5 +19,5 @@ public sealed partial class MobsDunGen : IDunGenLayer
|
||||
public int MaxCount = 1;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<EntitySpawnEntry> Groups = new();
|
||||
public ProtoId<EntityTablePrototype> Contents;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user