Skip to content

Commit

Permalink
Material Silo (#1488)
Browse files Browse the repository at this point in the history
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Port from [Goob](Goob-Station/Goob-Station#989)

---

# Changelog

<!--
You can add an author after the `:cl:` to change the name that appears
in the changelog (ex: `:cl: Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

:cl: Aviu00, Spatison
- add: Added material silo

---------

Signed-off-by: Spatison <[email protected]>
Signed-off-by: VMSolidus <[email protected]>
Co-authored-by: VMSolidus <[email protected]>
  • Loading branch information
Spatison and VMSolidus authored Jan 11, 2025
1 parent a050d4e commit 9897094
Show file tree
Hide file tree
Showing 32 changed files with 668 additions and 114 deletions.
5 changes: 5 additions & 0 deletions Content.Client/Materials/MaterialSiloSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.Materials;

namespace Content.Client.Materials;

public sealed class MaterialSiloSystem : SharedMaterialSiloSystem;
3 changes: 2 additions & 1 deletion Content.Client/Materials/MaterialStorageSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ public override bool TryInsertMaterialEntity(EntityUid user,
EntityUid toInsert,
EntityUid receiver,
MaterialStorageComponent? storage = null,
MaterialSiloUtilizerComponent? utilizer = null,
MaterialComponent? material = null,
PhysicalCompositionComponent? composition = null)
{
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, utilizer, material, composition))
return false;
_transform.DetachParentToNull(toInsert, Transform(toInsert));
return true;
Expand Down
1 change: 1 addition & 0 deletions Content.Client/Materials/UI/MaterialStorageControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer Name="MaterialList" Orientation="Vertical">
<Label Name="ConnectToSiloLabel" Text="{Loc 'lathe-menu-connected-to-silo-message'}" Align="Center"/>
<Label Name="NoMatsLabel" Text="{Loc 'lathe-menu-no-materials-message'}" Align="Center"/>
</BoxContainer>
</ScrollContainer>
21 changes: 20 additions & 1 deletion Content.Client/Materials/UI/MaterialStorageControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public sealed partial class MaterialStorageControl : ScrollContainer
{
[Dependency] private readonly IEntityManager _entityManager = default!;

private readonly MaterialSiloSystem _materialSilo;

private EntityUid? _owner;

private Dictionary<string, int> _currentMaterials = new();
Expand All @@ -23,6 +25,8 @@ public MaterialStorageControl()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

_materialSilo = _entityManager.System<MaterialSiloSystem>();
}

public void SetOwner(EntityUid owner)
Expand All @@ -44,7 +48,22 @@ protected override void FrameUpdate(FrameEventArgs args)
}

var canEject = materialStorage.CanEjectStoredMaterials;
var mats = materialStorage.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary();

Dictionary<string, int> mats;
if (_entityManager.TryGetComponent<MaterialSiloUtilizerComponent>(_owner, out var utilizer) && utilizer.Silo.HasValue)
{
var silo = _materialSilo.GetSiloStorage(_owner.Value);
mats = silo != null
? silo.Value.Comp.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary()
: materialStorage.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary();
ConnectToSiloLabel.Visible = silo != null;
}
else
{
mats = materialStorage.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary();
ConnectToSiloLabel.Visible = false;
}

if (_currentMaterials.Equals(mats))
return;

Expand Down
70 changes: 70 additions & 0 deletions Content.Server/Materials/MaterialSiloSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Linq;
using Content.Server.Lathe;
using Content.Server.Station.Components;
using Content.Shared.DeviceLinking;
using Content.Shared.Lathe;
using Content.Shared.Materials;
using Robust.Shared.Timing;

namespace Content.Server.Materials;

public sealed class MaterialSiloSystem : SharedMaterialSiloSystem
{
[Dependency] private readonly LatheSystem _lathe = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<BecomesStationComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<MaterialSiloComponent, MaterialAmountChangedEvent>(OnMaterialAmountChanged);
}

private void OnMaterialAmountChanged(Entity<MaterialSiloComponent> ent, ref MaterialAmountChangedEvent args)
{
// Spawning a timer because SetUiState in UpdateUserInterfaceState is being networked before
// silo's MaterialStorageComponent state gets handled.
// That causes lathe ui recipe list to not update properly.
Timer.Spawn(20,
() =>
{
if (!TryComp(ent, out DeviceLinkSourceComponent? source))
return;

foreach (var utilizerSet in source.Outputs.Where(x => x.Key == SourcePort).Select(x => x.Value))
{
foreach (var utilizer in utilizerSet)
{
if (TryComp(utilizer, out LatheComponent? lathe))
_lathe.UpdateUserInterfaceState(utilizer, lathe);
}
}
});
}

private void OnMapInit(Entity<BecomesStationComponent> ent, ref MapInitEvent args)
{
Entity<DeviceLinkSourceComponent>? silo = null;
var siloQuery = AllEntityQuery<MaterialSiloComponent, MaterialStorageComponent, TransformComponent, DeviceLinkSourceComponent>();
while (siloQuery.MoveNext(out var siloEnt, out _, out _, out var siloXform, out var source))
{
if (siloXform.GridUid != ent)
continue;

silo = (siloEnt, source);
break;
}

if (silo == null)
return;

var utilizerQuery = AllEntityQuery<MaterialSiloUtilizerComponent, MaterialStorageComponent, TransformComponent, DeviceLinkSinkComponent>();
while (utilizerQuery.MoveNext(out var utilizer, out _, out var storage, out var utilizerXform, out var sink))
{
if (utilizerXform.GridUid != ent)
continue;

DeviceLink.LinkDefaults(null, silo.Value, utilizer, silo.Value.Comp, sink);
}
}
}
17 changes: 11 additions & 6 deletions Content.Server/Materials/MaterialStorageSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,15 @@ public override bool TryInsertMaterialEntity(EntityUid user,
EntityUid toInsert,
EntityUid receiver,
MaterialStorageComponent? storage = null,
MaterialSiloUtilizerComponent? utilizer = 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))
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, utilizer, material, composition))
return false;
_audio.PlayPvs(storage.InsertingSound, receiver);
_popup.PopupEntity(Loc.GetString("machine-insert-item", ("user", user), ("machine", receiver),
Expand Down Expand Up @@ -187,26 +188,28 @@ public List<EntityUid> SpawnMultipleFromMaterial(int amount, MaterialPrototype m
/// <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>
/// <param name="utilizer">The material silo utilizer component on <paramref name="uid"/>.</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)
MaterialStorageComponent? component = null,
MaterialSiloUtilizerComponent? utilizer = null)
{
if (!Resolve(entity, ref component))
return new List<EntityUid>();

coordinates ??= Transform(entity).Coordinates;

var amount = GetMaterialAmount(entity, material, component);
var amount = GetMaterialAmount(entity, material, component, utilizer);
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);
TryChangeMaterialAmount(entity, material, -(amount - overflow), component, utilizer);
return spawned;
}

Expand All @@ -216,11 +219,13 @@ public List<EntityUid> EjectMaterial(
/// <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>
/// <param name="utilizer">The material silo utilizer component on <paramref name="uid"/>.</param>
/// <returns>The stack entities that were spawned.</returns>
public List<EntityUid> EjectAllMaterial(
EntityUid entity,
EntityCoordinates? coordinates = null,
MaterialStorageComponent? component = null)
MaterialStorageComponent? component = null,
MaterialSiloUtilizerComponent? utilizer = null)
{
if (!Resolve(entity, ref component))
return new List<EntityUid>();
Expand All @@ -230,7 +235,7 @@ public List<EntityUid> EjectAllMaterial(
var allSpawned = new List<EntityUid>();
foreach (var material in component.Storage.Keys.ToArray())
{
var spawned = EjectMaterial(entity, material, null, coordinates, component);
var spawned = EjectMaterial(entity, material, null, coordinates, component, utilizer);
allSpawned.AddRange(spawned);
}

Expand Down
11 changes: 11 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2980,6 +2980,17 @@ public static readonly CVarDef<float>
CVarDef.Create("reclaimer.allow_gibbing", true, CVar.SERVER);

#endregion

#region Material Silo

/// <summary>
/// Is ore material enabled.
/// </summary>
public static readonly CVarDef<bool> SiloEnabled =
CVarDef.Create("silo.silo_enabled", true, CVar.SERVER | CVar.REPLICATED);

#endregion

#region Jetpack System
/*
* Jetpack System
Expand Down
6 changes: 6 additions & 0 deletions Content.Shared/Materials/MaterialSiloComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Materials;

[RegisterComponent, NetworkedComponent]
public sealed partial class MaterialSiloComponent : Component;
10 changes: 10 additions & 0 deletions Content.Shared/Materials/MaterialSiloUtilizerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Robust.Shared.GameStates;

namespace Content.Shared.Materials;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class MaterialSiloUtilizerComponent : Component
{
[DataField, AutoNetworkedField]
public EntityUid? Silo;
}
1 change: 0 additions & 1 deletion Content.Shared/Materials/MaterialStorageComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
namespace Content.Shared.Materials;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedMaterialStorageSystem))]
public sealed partial class MaterialStorageComponent : Component
{
[DataField, AutoNetworkedField]
Expand Down
120 changes: 120 additions & 0 deletions Content.Shared/Materials/SharedMaterialSiloSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System.Linq;
using Content.Shared.CCVar;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Power;
using Content.Shared.Power.EntitySystems;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;

namespace Content.Shared.Materials;

public abstract class SharedMaterialSiloSystem : EntitySystem
{
[Dependency] protected readonly SharedDeviceLinkSystem DeviceLink = default!;

[Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
[Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!;

[Dependency] private readonly IConfigurationManager _cfg = default!;

private bool _siloEnabled;

protected ProtoId<SourcePortPrototype> SourcePort = "MaterialSilo";
protected ProtoId<SinkPortPrototype> SinkPort = "MaterialSiloUtilizer";

public override void Initialize()
{
base.Initialize();

_cfg.OnValueChanged(CCVars.SiloEnabled, enabled => _siloEnabled = enabled, true);

SubscribeLocalEvent<MaterialSiloComponent, NewLinkEvent>(OnNewLink);
SubscribeLocalEvent<MaterialSiloComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<MaterialSiloUtilizerComponent, PortDisconnectedEvent>(OnPortDisconnected);
}

private void OnPortDisconnected(Entity<MaterialSiloUtilizerComponent> ent, ref PortDisconnectedEvent args)
{
if (args.Port != SinkPort)
return;

ent.Comp.Silo = null;
Dirty(ent);
}

private void OnNewLink(Entity<MaterialSiloComponent> ent, ref NewLinkEvent args)
{
if (args.SinkPort != SinkPort || args.SourcePort != SourcePort
|| !TryComp(args.Sink, out MaterialSiloUtilizerComponent? utilizer))
return;

if (utilizer.Silo != null)
DeviceLink.RemoveSinkFromSource(utilizer.Silo.Value, args.Sink);

if (TryComp(args.Sink, out MaterialStorageComponent? utilizerStorage)
&& utilizerStorage.Storage.Count != 0
&& TryComp(ent, out MaterialStorageComponent? siloStorage))
{
foreach (var material in utilizerStorage.Storage.Keys.ToArray())
{
var materialAmount = utilizerStorage.Storage.GetValueOrDefault(material, 0);
if (_materialStorage.TryChangeMaterialAmount(ent, material, materialAmount, siloStorage))
_materialStorage.TryChangeMaterialAmount(args.Sink, material, -materialAmount, utilizerStorage);
}
}

utilizer.Silo = ent;
Dirty(args.Sink, utilizer);
}

private void OnPowerChanged(Entity<MaterialSiloComponent> ent, ref PowerChangedEvent args)
{
if (!TryComp(ent, out MaterialStorageComponent? siloStorage))
return;

var siloUtilizerQuery = AllEntityQuery<MaterialSiloUtilizerComponent, MaterialStorageComponent>();

while (siloUtilizerQuery.MoveNext(out var utilizerUid, out var utilizer, out var utilizerStorage))
{
if (utilizer.Silo != ent)
continue;

foreach (var material in utilizerStorage.Storage.Keys.ToArray())
{
var materialAmount = utilizerStorage.Storage.GetValueOrDefault(material, 0);
if (!_materialStorage.TryChangeMaterialAmount(ent, material, materialAmount, siloStorage))
continue;

utilizerStorage.Storage[material] -= materialAmount;

var ev = new MaterialAmountChangedEvent();
RaiseLocalEvent(utilizerUid, ref ev);

Dirty(utilizerUid, utilizerStorage);
}
}
}

public int GetSiloMaterialAmount(EntityUid machine, string material, MaterialSiloUtilizerComponent? utilizer = null)
{
var silo = GetSiloStorage(machine, utilizer);
return silo == null ? 0 : silo.Value.Comp.Storage.GetValueOrDefault(material, 0);
}

public int GetSiloTotalMaterialAmount(EntityUid machine, MaterialSiloUtilizerComponent? utilizer = null)
{
var silo = GetSiloStorage(machine, utilizer);
return silo == null ? 0 : silo.Value.Comp.Storage.Values.Sum();
}

public Entity<MaterialStorageComponent>? GetSiloStorage(EntityUid machine, MaterialSiloUtilizerComponent? utilizer = null)
{
if (!_siloEnabled || !Resolve(machine, ref utilizer)
|| !TryComp(utilizer.Silo, out MaterialStorageComponent? storage)
|| !_powerReceiver.IsPowered(utilizer.Silo.Value))
return null;

return (utilizer.Silo.Value, storage);
}
}
Loading

0 comments on commit 9897094

Please sign in to comment.