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:
Galactic Chimp
2021-07-30 20:26:22 +02:00
117 changed files with 1807 additions and 1851 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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