| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- using System.Linq;
- using Content.Server.Administration.Logs;
- using Content.Shared.Materials;
- using Content.Shared.Popups;
- using Content.Shared.Stacks;
- using Content.Server.Power.Components;
- using Content.Server.Stack;
- using Content.Shared.ActionBlocker;
- using Content.Shared.Construction;
- using Content.Shared.Database;
- using JetBrains.Annotations;
- using Robust.Shared.Audio.Systems;
- using Robust.Shared.Map;
- using Robust.Shared.Prototypes;
- namespace Content.Server.Materials;
- /// <summary>
- /// This handles <see cref="SharedMaterialStorageSystem"/>
- /// </summary>
- public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
- {
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly StackSystem _stackSystem = default!;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<MaterialStorageComponent, MachineDeconstructedEvent>(OnDeconstructed);
- SubscribeAllEvent<EjectMaterialMessage>(OnEjectMessage);
- }
- private void OnDeconstructed(EntityUid uid, MaterialStorageComponent component, MachineDeconstructedEvent args)
- {
- if (!component.DropOnDeconstruct)
- return;
- foreach (var (material, amount) in component.Storage)
- {
- SpawnMultipleFromMaterial(amount, material, Transform(uid).Coordinates);
- }
- }
- private void OnEjectMessage(EjectMaterialMessage msg, EntitySessionEventArgs args)
- {
- if (args.SenderSession.AttachedEntity is not { } player)
- return;
- var uid = GetEntity(msg.Entity);
- if (!TryComp<MaterialStorageComponent>(uid, out var component))
- return;
- if (!Exists(uid))
- return;
- if (!_actionBlocker.CanInteract(player, uid))
- return;
- if (!component.CanEjectStoredMaterials || !_prototypeManager.TryIndex<MaterialPrototype>(msg.Material, out var material))
- return;
- var volume = 0;
- if (material.StackEntity != null)
- {
- if (!_prototypeManager.Index<EntityPrototype>(material.StackEntity).TryGetComponent<PhysicalCompositionComponent>(out var composition, EntityManager.ComponentFactory))
- return;
- var volumePerSheet = composition.MaterialComposition.FirstOrDefault(kvp => kvp.Key == msg.Material).Value;
- var sheetsToExtract = Math.Min(msg.SheetsToExtract, _stackSystem.GetMaxCount(material.StackEntity));
- volume = sheetsToExtract * volumePerSheet;
- }
- if (volume <= 0 || !TryChangeMaterialAmount(uid, msg.Material, -volume))
- return;
- var mats = SpawnMultipleFromMaterial(volume, material, Transform(uid).Coordinates, out _);
- foreach (var mat in mats.Where(mat => !TerminatingOrDeleted(mat)))
- {
- _stackSystem.TryMergeToContacts(mat);
- }
- }
- public override bool TryInsertMaterialEntity(EntityUid user,
- EntityUid toInsert,
- EntityUid receiver,
- MaterialStorageComponent? storage = null,
- MaterialComponent? material = null,
- PhysicalCompositionComponent? composition = null)
- {
- if (!Resolve(receiver, ref storage) || !Resolve(toInsert, ref material, ref composition, false))
- return false;
- if (TryComp<ApcPowerReceiverComponent>(receiver, out var power) && !power.Powered)
- return false;
- if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
- return false;
- _audio.PlayPvs(storage.InsertingSound, receiver);
- _popup.PopupEntity(Loc.GetString("machine-insert-item", ("user", user), ("machine", receiver),
- ("item", toInsert)), receiver);
- QueueDel(toInsert);
- // Logging
- TryComp<StackComponent>(toInsert, out var stack);
- var count = stack?.Count ?? 1;
- _adminLogger.Add(LogType.Action, LogImpact.Low,
- $"{ToPrettyString(user):player} inserted {count} {ToPrettyString(toInsert):inserted} into {ToPrettyString(receiver):receiver}");
- return true;
- }
- /// <summary>
- /// Spawn an amount of a material in stack entities.
- /// Note the 'amount' is material dependent.
- /// 1 biomass = 1 biomass in its stack,
- /// but 100 plasma = 1 sheet of plasma, etc.
- /// </summary>
- public List<EntityUid> SpawnMultipleFromMaterial(int amount, string material, EntityCoordinates coordinates)
- {
- return SpawnMultipleFromMaterial(amount, material, coordinates, out _);
- }
- /// <summary>
- /// Spawn an amount of a material in stack entities.
- /// Note the 'amount' is material dependent.
- /// 1 biomass = 1 biomass in its stack,
- /// but 100 plasma = 1 sheet of plasma, etc.
- /// </summary>
- public List<EntityUid> SpawnMultipleFromMaterial(int amount, string material, EntityCoordinates coordinates, out int overflowMaterial)
- {
- overflowMaterial = 0;
- if (!_prototypeManager.TryIndex<MaterialPrototype>(material, out var stackType))
- {
- Log.Error("Failed to index material prototype " + material);
- return new List<EntityUid>();
- }
- return SpawnMultipleFromMaterial(amount, stackType, coordinates, out overflowMaterial);
- }
- /// <summary>
- /// Spawn an amount of a material in stack entities.
- /// Note the 'amount' is material dependent.
- /// 1 biomass = 1 biomass in its stack,
- /// but 100 plasma = 1 sheet of plasma, etc.
- /// </summary>
- [PublicAPI]
- public List<EntityUid> SpawnMultipleFromMaterial(int amount, MaterialPrototype materialProto, EntityCoordinates coordinates)
- {
- return SpawnMultipleFromMaterial(amount, materialProto, coordinates, out _);
- }
- /// <summary>
- /// Spawn an amount of a material in stack entities.
- /// Note the 'amount' is material dependent.
- /// 1 biomass = 1 biomass in its stack,
- /// but 100 plasma = 1 sheet of plasma, etc.
- /// </summary>
- public List<EntityUid> SpawnMultipleFromMaterial(int amount, MaterialPrototype materialProto, EntityCoordinates coordinates, out int overflowMaterial)
- {
- overflowMaterial = 0;
- if (amount <= 0 || materialProto.StackEntity == null)
- return new List<EntityUid>();
- var entProto = _prototypeManager.Index<EntityPrototype>(materialProto.StackEntity);
- if (!entProto.TryGetComponent<PhysicalCompositionComponent>(out var composition, EntityManager.ComponentFactory))
- return new List<EntityUid>();
- var materialPerStack = composition.MaterialComposition[materialProto.ID];
- var amountToSpawn = amount / materialPerStack;
- overflowMaterial = amount - amountToSpawn * materialPerStack;
- if (amountToSpawn == 0)
- return new List<EntityUid>();
- return _stackSystem.SpawnMultiple(materialProto.StackEntity, amountToSpawn, coordinates);
- }
- /// <summary>
- /// Eject a material out of this storage. The internal counts are updated.
- /// Material that cannot be ejected stays in storage. (e.g. only have 50 but a sheet needs 100).
- /// </summary>
- /// <param name="entity">The entity with storage to eject from.</param>
- /// <param name="material">The material prototype to eject.</param>
- /// <param name="maxAmount">The maximum amount to eject. If not given, as much as possible is ejected.</param>
- /// <param name="coordinates">The position where to spawn the created sheets. If not given, they're spawned next to the entity.</param>
- /// <param name="component">The storage component on <paramref name="entity"/>. Resolved automatically if not given.</param>
- /// <returns>The stack entities that were spawned.</returns>
- public List<EntityUid> EjectMaterial(
- EntityUid entity,
- string material,
- int? maxAmount = null,
- EntityCoordinates? coordinates = null,
- MaterialStorageComponent? component = null)
- {
- if (!Resolve(entity, ref component))
- return new List<EntityUid>();
- coordinates ??= Transform(entity).Coordinates;
- var amount = GetMaterialAmount(entity, material, component);
- if (maxAmount != null)
- amount = Math.Min(maxAmount.Value, amount);
- var spawned = SpawnMultipleFromMaterial(amount, material, coordinates.Value, out var overflow);
- TryChangeMaterialAmount(entity, material, -(amount - overflow), component);
- return spawned;
- }
- /// <summary>
- /// Eject all material stored in an entity, with the same mechanics as <see cref="EjectMaterial"/>.
- /// </summary>
- /// <param name="entity">The entity with storage to eject from.</param>
- /// <param name="coordinates">The position where to spawn the created sheets. If not given, they're spawned next to the entity.</param>
- /// <param name="component">The storage component on <paramref name="entity"/>. Resolved automatically if not given.</param>
- /// <returns>The stack entities that were spawned.</returns>
- public List<EntityUid> EjectAllMaterial(
- EntityUid entity,
- EntityCoordinates? coordinates = null,
- MaterialStorageComponent? component = null)
- {
- if (!Resolve(entity, ref component))
- return new List<EntityUid>();
- coordinates ??= Transform(entity).Coordinates;
- var allSpawned = new List<EntityUid>();
- foreach (var material in component.Storage.Keys.ToArray())
- {
- var spawned = EjectMaterial(entity, material, null, coordinates, component);
- allSpawned.AddRange(spawned);
- }
- return allSpawned;
- }
- }
|