Merge branch 'master' into replace-sounds-with-sound-specifier
# Conflicts: # Content.Server/Kitchen/Components/ReagentGrinderComponent.cs # Content.Server/Storage/Components/SecureEntityStorageComponent.cs
This commit is contained in:
@@ -25,7 +25,6 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -132,7 +131,8 @@ namespace Content.Server.Storage.Components
|
||||
private bool _beingWelded;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanWeldShut {
|
||||
public bool CanWeldShut
|
||||
{
|
||||
get => _canWeldShut;
|
||||
set
|
||||
{
|
||||
@@ -161,6 +161,13 @@ namespace Content.Server.Storage.Components
|
||||
|
||||
public virtual void Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
// HACK until EntityStorageComponent gets refactored to the new ECS system
|
||||
if (Owner.TryGetComponent<LockComponent>(out var @lock) && @lock.Locked)
|
||||
{
|
||||
// Do nothing, LockSystem is responsible for handling this case
|
||||
return;
|
||||
}
|
||||
|
||||
ToggleOpen(eventArgs.User);
|
||||
}
|
||||
|
||||
@@ -168,7 +175,7 @@ namespace Content.Server.Storage.Components
|
||||
{
|
||||
if (IsWeldedShut)
|
||||
{
|
||||
if(!silent) Owner.PopupMessage(user, Loc.GetString("entity-storage-component-welded-shut-message"));
|
||||
if (!silent) Owner.PopupMessage(user, Loc.GetString("entity-storage-component-welded-shut-message"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -468,7 +475,8 @@ namespace Content.Server.Storage.Components
|
||||
|
||||
protected virtual void OpenVerbGetData(IEntity user, EntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
|
||||
component.Owner.TryGetComponent(out LockComponent? lockComponent) && lockComponent.Locked) // HACK extra check, until EntityStorage gets refactored
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
@@ -478,7 +486,7 @@ namespace Content.Server.Storage.Components
|
||||
{
|
||||
data.Visibility = VerbVisibility.Disabled;
|
||||
var verb = Loc.GetString(component.Open ? "open-toggle-verb-close" : "open-toggle-verb-open");
|
||||
data.Text = Loc.GetString("open-toggle-verb-welded-shut-message",("verb", verb));
|
||||
data.Text = Loc.GetString("open-toggle-verb-welded-shut-message", ("verb", verb));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Content.Server.Storage.Components
|
||||
if (_itemContainer.ContainedEntity == null)
|
||||
return false;
|
||||
|
||||
Owner.PopupMessage(user, Loc.GetString("There was something inside {0}!", ("stash", SecretPartName)));
|
||||
Owner.PopupMessage(user, Loc.GetString("comp-secret-stash-action-get-item-found-something", ("stash", SecretPartName)));
|
||||
|
||||
if (user.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Storage.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(EntityStorageComponent))]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IStorageComponent))]
|
||||
public class SecureEntityStorageComponent : EntityStorageComponent
|
||||
{
|
||||
public override string Name => "SecureEntityStorage";
|
||||
[DataField("locked")]
|
||||
private bool _locked = true;
|
||||
|
||||
[DataField("unlockSound")] private SoundSpecifier _unlockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||
[DataField("lockSound")] private SoundSpecifier _lockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_on.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Locked
|
||||
{
|
||||
get => _locked;
|
||||
set
|
||||
{
|
||||
_locked = value;
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(StorageVisuals.Locked, _locked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(StorageVisuals.CanLock, true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
DoToggleLock(eventArgs.User);
|
||||
return;
|
||||
}
|
||||
|
||||
base.Activate(eventArgs);
|
||||
}
|
||||
|
||||
public override bool CanOpen(IEntity user, bool silent = false)
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
Owner.PopupMessage(user, "It's locked!");
|
||||
return false;
|
||||
}
|
||||
return base.CanOpen(user, silent);
|
||||
}
|
||||
|
||||
protected override void OpenVerbGetData(IEntity user, EntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
base.OpenVerbGetData(user, component, data);
|
||||
}
|
||||
|
||||
private void DoToggleLock(IEntity user)
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
DoUnlock(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoLock(user);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoUnlock(IEntity user)
|
||||
{
|
||||
if (!CheckAccess(user)) return;
|
||||
|
||||
Locked = false;
|
||||
if(_unlockSound.TryGetSound(out var unlockSound))
|
||||
SoundSystem.Play(Filter.Pvs(Owner), unlockSound, Owner, AudioParams.Default.WithVolume(-5));
|
||||
}
|
||||
|
||||
private void DoLock(IEntity user)
|
||||
{
|
||||
if (!CheckAccess(user)) return;
|
||||
|
||||
Locked = true;
|
||||
if(_lockSound.TryGetSound(out var lockSound))
|
||||
SoundSystem.Play(Filter.Pvs(Owner), lockSound, Owner, AudioParams.Default.WithVolume(-5));
|
||||
}
|
||||
|
||||
private bool CheckAccess(IEntity user)
|
||||
{
|
||||
if (Owner.TryGetComponent(out AccessReader? reader))
|
||||
{
|
||||
if (!reader.IsAllowed(user))
|
||||
{
|
||||
Owner.PopupMessage(user, Loc.GetString("secure-entity-storage-component-not-allowed-message"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class ToggleLockVerb : Verb<SecureEntityStorageComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, SecureEntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) || component.Open)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString(component.Locked ? "toggle-lock-verb-unlock" : "toggle-lock-verb-lock");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, SecureEntityStorageComponent component)
|
||||
{
|
||||
component.DoToggleLock(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -48,10 +49,16 @@ namespace Content.Server.Storage.Components
|
||||
|
||||
[DataField("occludesLight")]
|
||||
private bool _occludesLight = true;
|
||||
|
||||
[DataField("quickInsert")]
|
||||
private bool _quickInsert; //Can insert storables by "attacking" them with the storage entity
|
||||
private bool _quickInsert = false; // Can insert storables by "attacking" them with the storage entity
|
||||
|
||||
[DataField("areaInsert")]
|
||||
private bool _areaInsert; //"Attacking" with the storage entity causes it to insert all nearby storables after a delay
|
||||
private bool _areaInsert = false; // "Attacking" with the storage entity causes it to insert all nearby storables after a delay
|
||||
|
||||
[DataField("whitelist")]
|
||||
private EntityWhitelist? _whitelist = null;
|
||||
|
||||
private bool _storageInitialCalculated;
|
||||
private int _storageUsed;
|
||||
[DataField("capacity")]
|
||||
@@ -124,6 +131,11 @@ namespace Content.Server.Storage.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_whitelist != null && !_whitelist.IsValid(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Storage.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns items when used in hand.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class SpawnItemsOnUseComponent : Component
|
||||
{
|
||||
public override string Name => "SpawnItemsOnUse";
|
||||
|
||||
/// <summary>
|
||||
/// The list of entities to spawn, with amounts and orGroups.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("items", required: true)]
|
||||
public List<EntitySpawnEntry> Items = new List<EntitySpawnEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// A sound to play when the items are spawned. For example, gift boxes being unwrapped.
|
||||
/// </summary>
|
||||
[DataField("sound")]
|
||||
public SoundSpecifier? Sound = null;
|
||||
|
||||
/// <summary>
|
||||
/// How many uses before the item should delete itself.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField("uses")]
|
||||
public int Uses = 1;
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ namespace Content.Server.Storage.Components
|
||||
{
|
||||
public override string Name => "StorageFill";
|
||||
|
||||
[DataField("contents")] private List<StorageFillEntry> _contents = new();
|
||||
[DataField("contents")] private List<EntitySpawnEntry> _contents = new();
|
||||
|
||||
public IReadOnlyList<StorageFillEntry> Contents => _contents;
|
||||
public IReadOnlyList<EntitySpawnEntry> Contents => _contents;
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
@@ -39,7 +39,6 @@ namespace Content.Server.Storage.Components
|
||||
var alreadySpawnedGroups = new List<string>();
|
||||
foreach (var storageItem in _contents)
|
||||
{
|
||||
if (string.IsNullOrEmpty(storageItem.PrototypeId)) continue;
|
||||
if (!string.IsNullOrEmpty(storageItem.GroupId) &&
|
||||
alreadySpawnedGroups.Contains(storageItem.GroupId)) continue;
|
||||
|
||||
@@ -58,50 +57,5 @@ namespace Content.Server.Storage.Components
|
||||
if (!string.IsNullOrEmpty(storageItem.GroupId)) alreadySpawnedGroups.Add(storageItem.GroupId);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public struct StorageFillEntry : IPopulateDefaultValues
|
||||
{
|
||||
[DataField("id", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? PrototypeId;
|
||||
|
||||
[DataField("prob")] public float SpawnProbability;
|
||||
|
||||
/// <summary>
|
||||
/// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc.
|
||||
/// </summary>
|
||||
[DataField("orGroup")] public string GroupId;
|
||||
|
||||
/// <summary>
|
||||
/// orGroup signifies to pick between entities designated with an ID.
|
||||
///
|
||||
/// <example>
|
||||
/// <para>To define an orGroup in a StorageFill component you
|
||||
/// need to add it to the entities you want to choose between and
|
||||
/// add a prob field. In this example there is a 50% chance the storage
|
||||
/// spawns with Y or Z.
|
||||
///
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// - type: StorageFill
|
||||
/// contents:
|
||||
/// - name: X
|
||||
/// - name: Y
|
||||
/// prob: 0.50
|
||||
/// orGroup: YOrZ
|
||||
/// - name: Z
|
||||
/// orGroup: YOrZ
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
[DataField("amount")] public int Amount;
|
||||
|
||||
public void PopulateDefaultValues()
|
||||
{
|
||||
Amount = 1;
|
||||
SpawnProbability = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
Content.Server/Storage/EntitySpawnEntry.cs
Normal file
59
Content.Server/Storage/EntitySpawnEntry.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Storage
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictates a list of items that can be spawned.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public struct EntitySpawnEntry : IPopulateDefaultValues
|
||||
{
|
||||
[DataField("id", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string PrototypeId;
|
||||
|
||||
/// <summary>
|
||||
/// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc.
|
||||
/// </summary>
|
||||
[DataField("prob")]
|
||||
public float SpawnProbability;
|
||||
|
||||
/// <summary>
|
||||
/// orGroup signifies to pick between entities designated with an ID.
|
||||
///
|
||||
/// <example>
|
||||
/// <para>To define an orGroup in a StorageFill component you
|
||||
/// need to add it to the entities you want to choose between and
|
||||
/// add a prob field. In this example there is a 50% chance the storage
|
||||
/// spawns with Y or Z.
|
||||
///
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// - type: StorageFill
|
||||
/// contents:
|
||||
/// - name: X
|
||||
/// - name: Y
|
||||
/// prob: 0.50
|
||||
/// orGroup: YOrZ
|
||||
/// - name: Z
|
||||
/// orGroup: YOrZ
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
[DataField("orGroup")]
|
||||
public string? GroupId;
|
||||
|
||||
[DataField("amount")]
|
||||
public int Amount;
|
||||
|
||||
public void PopulateDefaultValues()
|
||||
{
|
||||
Amount = 1;
|
||||
SpawnProbability = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Storage
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ItemCounterSystem : SharedItemCounterSystem
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
public class SpawnItemsOnUseSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpawnItemsOnUseComponent, UseInHandEvent>(OnUseInHand);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, SpawnItemsOnUseComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var owner = EntityManager.GetEntity(uid);
|
||||
var alreadySpawnedGroups = new List<string>();
|
||||
IEntity? entityToPlaceInHands = null;
|
||||
foreach (var storageItem in component.Items)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(storageItem.GroupId) &&
|
||||
alreadySpawnedGroups.Contains(storageItem.GroupId)) continue;
|
||||
|
||||
if (storageItem.SpawnProbability != 1f &&
|
||||
!_random.Prob(storageItem.SpawnProbability))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = 0; i < storageItem.Amount; i++)
|
||||
{
|
||||
entityToPlaceInHands = EntityManager.SpawnEntity(storageItem.PrototypeId, args.User.Transform.Coordinates);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(storageItem.GroupId)) alreadySpawnedGroups.Add(storageItem.GroupId);
|
||||
}
|
||||
|
||||
if (component.Sound != null)
|
||||
SoundSystem.Play(Filter.Pvs(owner), component.Sound.GetSound());
|
||||
|
||||
component.Uses--;
|
||||
if (component.Uses == 0)
|
||||
{
|
||||
args.Handled = true;
|
||||
owner.Delete();
|
||||
}
|
||||
|
||||
if (entityToPlaceInHands != null
|
||||
&& args.User.TryGetComponent<SharedHandsComponent>(out var hands))
|
||||
{
|
||||
hands.TryPutInAnyHand(entityToPlaceInHands);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Storage
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class StorageSystem : EntitySystem
|
||||
@@ -54,7 +54,7 @@ namespace Content.Server.Storage
|
||||
{
|
||||
storageComp.HandleEntityMaybeInserted(message);
|
||||
}
|
||||
|
||||
|
||||
if (oldParentEntity.TryGetComponent<StorageCounterComponent>(out var newStorageComp))
|
||||
{
|
||||
newStorageComp.ContainerUpdateAppearance(message.Container);
|
||||
Reference in New Issue
Block a user