mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
* MaterialReclaimerSystem: cleanup * more cleanup --------- Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com>
287 lines
10 KiB
C#
287 lines
10 KiB
C#
using System.Linq;
|
|
using Content.Shared.Administration.Logs;
|
|
using Content.Shared.Audio;
|
|
using Content.Shared.Body;
|
|
using Content.Shared.Chemistry.Components;
|
|
using Content.Shared.Chemistry.EntitySystems;
|
|
using Content.Shared.Construction.EntitySystems;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.Emag.Systems;
|
|
using Content.Shared.Examine;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Mobs.Components;
|
|
using Content.Shared.Nutrition.EntitySystems;
|
|
using Content.Shared.Stacks;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Physics.Events;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Shared.Materials;
|
|
|
|
/// <summary>
|
|
/// Handles interactions and logic related to <see cref="MaterialReclaimerComponent"/>,
|
|
/// <see cref="CollideMaterialReclaimerComponent"/>, and <see cref="ActiveMaterialReclaimerComponent"/>.
|
|
/// </summary>
|
|
public abstract partial class SharedMaterialReclaimerSystem : EntitySystem
|
|
{
|
|
[Dependency] private EmagSystem _emag = default!;
|
|
[Dependency] private EntityWhitelistSystem _whitelist = default!;
|
|
[Dependency] private ISharedAdminLogManager _adminLog = default!;
|
|
[Dependency] private OpenableSystem _openable = default!;
|
|
[Dependency] private SharedAudioSystem _audio = default!;
|
|
[Dependency] private SharedSolutionContainerSystem _solutionContainer = default!;
|
|
[Dependency] protected IGameTiming Timing = default!;
|
|
[Dependency] protected SharedAmbientSoundSystem AmbientSound = default!;
|
|
[Dependency] protected SharedContainerSystem Container = default!;
|
|
|
|
[Dependency] private EntityQuery<MaterialReclaimerComponent> _materialReclaimerQuery;
|
|
[Dependency] private EntityQuery<SolutionTransferComponent> _solutionTransferQuery;
|
|
|
|
public const string ActiveReclaimerContainerId = "active-material-reclaimer-container";
|
|
|
|
[SubscribeLocalEvent(before: [typeof(SolutionTransferSystem), typeof(AnchorableSystem)])]
|
|
private void OnInteractUsing(Entity<MaterialReclaimerComponent> entity, ref InteractUsingEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
// if we're trying to get a solution out of the reclaimer, don't destroy it
|
|
if (entity.Comp.SolutionContainerId != null && _solutionContainer.TryGetSolution(entity.Owner, entity.Comp.SolutionContainerId, out _, out var outputSolution) && outputSolution.Contents.Any())
|
|
{
|
|
if (_solutionContainer.EnumerateSolutions(args.Used).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
|
|
{
|
|
if (_openable.IsClosed(args.Used))
|
|
return;
|
|
|
|
if (_solutionTransferQuery.TryComp(args.Used, out var transfer) &&
|
|
transfer.CanSend)
|
|
return;
|
|
}
|
|
}
|
|
|
|
args.Handled = TryStartProcessItem(entity.Owner, args.Used, entity.Comp, args.User);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnMapInit(Entity<MaterialReclaimerComponent> ent, ref MapInitEvent args)
|
|
{
|
|
ent.Comp.NextSound = Timing.CurTime;
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnShutdown(Entity<MaterialReclaimerComponent> ent, ref ComponentShutdown args)
|
|
{
|
|
_audio.Stop(ent.Comp.Stream);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnExamined(Entity<MaterialReclaimerComponent> ent, ref ExaminedEvent args)
|
|
{
|
|
args.PushMarkup(Loc.GetString("recycler-count-items", ("items", ent.Comp.ItemsProcessed)));
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnEmagged(Entity<MaterialReclaimerComponent> ent, ref GotEmaggedEvent args)
|
|
{
|
|
if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
|
|
return;
|
|
|
|
if (_emag.CheckFlag(ent, EmagType.Interaction))
|
|
return;
|
|
|
|
args.Handled = true;
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnCollide(Entity<CollideMaterialReclaimerComponent> ent, ref StartCollideEvent args)
|
|
{
|
|
if (args.OurFixtureId != ent.Comp.FixtureId)
|
|
return;
|
|
if (!_materialReclaimerQuery.TryComp(ent, out var reclaimer))
|
|
return;
|
|
TryStartProcessItem(ent, args.OtherEntity, reclaimer);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnActiveStartup(Entity<ActiveMaterialReclaimerComponent> ent, ref ComponentStartup args)
|
|
{
|
|
ent.Comp.ReclaimingContainer = Container.EnsureContainer<Container>(ent, ActiveReclaimerContainerId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to start processing an item via a <see cref="MaterialReclaimerComponent"/>.
|
|
/// </summary>
|
|
public bool TryStartProcessItem(EntityUid uid, EntityUid item, MaterialReclaimerComponent? component = null, EntityUid? user = null)
|
|
{
|
|
if (!Resolve(uid, ref component))
|
|
return false;
|
|
|
|
if (!CanStart(uid, component))
|
|
return false;
|
|
|
|
if (HasComp<MobStateComponent>(item) && !CanDamageAndGib(uid, item, component)) // whitelist? We be gibbing, boy!
|
|
return false;
|
|
|
|
if (_whitelist.IsWhitelistFail(component.Whitelist, item) ||
|
|
_whitelist.IsWhitelistPass(component.Blacklist, item))
|
|
return false;
|
|
|
|
if (Container.TryGetContainingContainer((item, null, null), out _) && !Container.TryRemoveFromContainer(item))
|
|
return false;
|
|
|
|
if (user != null)
|
|
{
|
|
_adminLog.Add(LogType.Action,
|
|
LogImpact.High,
|
|
$"{ToPrettyString(user.Value):player} destroyed {ToPrettyString(item)} in the material reclaimer, {ToPrettyString(uid)}");
|
|
}
|
|
|
|
if (Timing.CurTime > component.NextSound)
|
|
{
|
|
component.Stream = _audio.PlayPredicted(component.Sound, uid, user)?.Entity;
|
|
component.NextSound = Timing.CurTime + component.SoundCooldown;
|
|
}
|
|
|
|
var reclaimedEvent = new GotReclaimedEvent(Transform(uid).Coordinates);
|
|
RaiseLocalEvent(item, ref reclaimedEvent);
|
|
|
|
var duration = GetReclaimingDuration(uid, item, component);
|
|
// if it's instant, don't bother with all the active comp stuff.
|
|
if (duration == TimeSpan.Zero)
|
|
{
|
|
Reclaim(uid, item, 1, component);
|
|
return true;
|
|
}
|
|
|
|
var active = EnsureComp<ActiveMaterialReclaimerComponent>(uid);
|
|
active.Duration = duration;
|
|
active.EndTime = Timing.CurTime + duration;
|
|
Container.Insert(item, active.ReclaimingContainer);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finishes processing an item, freeing up the the reclaimer.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This doesn't reclaim the entity itself, but rather ends the formal
|
|
/// process started with <see cref="ActiveMaterialReclaimerComponent"/>.
|
|
/// The actual reclaiming happens in <see cref="Reclaim"/>
|
|
/// </remarks>
|
|
public virtual bool TryFinishProcessItem(EntityUid uid, MaterialReclaimerComponent? component = null, ActiveMaterialReclaimerComponent? active = null)
|
|
{
|
|
if (!Resolve(uid, ref component, ref active, false))
|
|
return false;
|
|
|
|
RemCompDeferred(uid, active);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Spawns the materials and chemicals associated
|
|
/// with an entity. Also deletes the item.
|
|
/// </summary>
|
|
public virtual void Reclaim(EntityUid uid,
|
|
EntityUid item,
|
|
float completion = 1f,
|
|
MaterialReclaimerComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component))
|
|
return;
|
|
|
|
component.ItemsProcessed++;
|
|
if (component.CutOffSound)
|
|
{
|
|
_audio.Stop(component.Stream);
|
|
}
|
|
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the Enabled field on the reclaimer.
|
|
/// </summary>
|
|
public bool SetReclaimerEnabled(EntityUid uid, bool enabled, MaterialReclaimerComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component, false))
|
|
return true;
|
|
|
|
if (component.Broken && enabled)
|
|
return false;
|
|
|
|
component.Enabled = enabled;
|
|
AmbientSound.SetAmbience(uid, enabled && component.Powered);
|
|
Dirty(uid, component);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not the specified reclaimer can currently
|
|
/// begin reclaiming another entity.
|
|
/// </summary>
|
|
public bool CanStart(EntityUid uid, MaterialReclaimerComponent component)
|
|
{
|
|
if (HasComp<ActiveMaterialReclaimerComponent>(uid))
|
|
return false;
|
|
|
|
return component.Powered && component.Enabled && !component.Broken;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether or not the reclaimer satisfies the conditions
|
|
/// allowing it to gib/reclaim a living creature.
|
|
/// </summary>
|
|
public bool CanDamageAndGib(EntityUid uid, EntityUid victim, MaterialReclaimerComponent component)
|
|
{
|
|
return component.Powered &&
|
|
component.Enabled &&
|
|
!component.Broken &&
|
|
HasComp<BodyComponent>(victim) &&
|
|
_emag.CheckFlag(uid, EmagType.Interaction);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the duration of processing a specified entity.
|
|
/// Processing is calculated from the sum of the materials within the entity.
|
|
/// It does not regard the chemicals within it.
|
|
/// </summary>
|
|
public TimeSpan GetReclaimingDuration(EntityUid reclaimer,
|
|
EntityUid item,
|
|
MaterialReclaimerComponent? reclaimerComponent = null,
|
|
PhysicalCompositionComponent? compositionComponent = null)
|
|
{
|
|
if (!Resolve(reclaimer, ref reclaimerComponent))
|
|
return TimeSpan.Zero;
|
|
|
|
if (!reclaimerComponent.ScaleProcessSpeed ||
|
|
!Resolve(item, ref compositionComponent, false))
|
|
return reclaimerComponent.MinimumProcessDuration;
|
|
|
|
var materialSum = compositionComponent.MaterialComposition.Values.Sum();
|
|
materialSum *= CompOrNull<StackComponent>(item)?.Count ?? 1;
|
|
var duration = TimeSpan.FromSeconds(materialSum / reclaimerComponent.MaterialProcessRate);
|
|
if (duration < reclaimerComponent.MinimumProcessDuration)
|
|
duration = reclaimerComponent.MinimumProcessDuration;
|
|
return duration;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
var query = EntityQueryEnumerator<ActiveMaterialReclaimerComponent, MaterialReclaimerComponent>();
|
|
while (query.MoveNext(out var uid, out var active, out var reclaimer))
|
|
{
|
|
if (Timing.CurTime < active.EndTime)
|
|
continue;
|
|
TryFinishProcessItem(uid, reclaimer, active);
|
|
}
|
|
}
|
|
}
|
|
|
|
[ByRefEvent]
|
|
public record struct GotReclaimedEvent(EntityCoordinates ReclaimerCoordinates);
|