Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Shared._DEN.ReagentProduction.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

namespace Content.Shared._DEN.ReagentProduction.Components;
/// <summary>
/// Entities with this component produce reagents based on
/// what types of <see cref="ReagentProductionTypePrototype"/> this component has.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ReagentProducerComponent : Component
Comment thread
honeyed-lemons marked this conversation as resolved.
{
/// <summary>
/// A list of production types this component manages.
/// </summary>
[DataField, AutoNetworkedField]
public List<ProtoId<ReagentProductionTypePrototype>> ProductionTypes = [];
Comment thread
honeyed-lemons marked this conversation as resolved.

/// <summary>
/// The next time to fill solution
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan NextUpdate;

/// <summary>
/// The interval between updates.
/// </summary>
[DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(10);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using System.Globalization;
using Content.Shared._DEN.ReagentProduction.Components;
using Content.Shared._DEN.ReagentProduction.Events;
using Content.Shared._DEN.ReagentProduction.Prototypes;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Enumerable = System.Linq.Enumerable;
using static Content.Shared._DEN.ReagentProduction.Events.ReagentProductionEvents;

namespace Content.Shared._DEN.ReagentProduction.EntitySystems;

public sealed class ReagentProductionSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;

Check failure on line 25 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

Field '_gameTiming' is a

Check failure on line 25 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Field '_gameTiming' is a
[Dependency] private readonly IPrototypeManager _protoManager = default!;

Check failure on line 26 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

Field '_protoManager' is a

Check failure on line 26 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Field '_protoManager' is a
[Dependency] private readonly MobStateSystem _mobState = default!;

Check failure on line 27 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

Field '_mobState' is a

Check failure on line 27 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Field '_mobState' is a
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;

Check failure on line 28 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

Field '_doAfter' is a

Check failure on line 28 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Field '_doAfter' is a
[Dependency] private readonly SharedPopupSystem _popup = default!;

Check failure on line 29 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

Field '_popup' is a

Check failure on line 29 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Field '_popup' is a
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;

public static readonly VerbCategory ReagentFillCategory = new("verb-categories-fill", "/Textures/Interface/VerbIcons/spill.svg.192dpi.png");

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReagentProducerComponent, ReagentProductionTypeAdded>(ProductionTypeAdded);
SubscribeLocalEvent<ReagentProducerComponent, ReagentProductionTypeRemoved>(ProductionTypeRemoved);

SubscribeLocalEvent<RefillableSolutionComponent, GetVerbsEvent<InteractionVerb>>(AddVerbs);

SubscribeLocalEvent<ReagentProducerComponent, ReagentProductionFillEvent>(FinishFillDoAfter);
SubscribeLocalEvent<ReagentProducerComponent, MapInitEvent>(OnMapInit);
}

private void OnMapInit(Entity<ReagentProducerComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
}

public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ReagentProducerComponent, SolutionContainerManagerComponent>();
while (query.MoveNext(out var uid, out var producerComponent, out _))
{
// If the mob is dead OR it isnt time for the next update, don't move foward
if (!_mobState.IsAlive(uid) || _gameTiming.CurTime < producerComponent.NextUpdate)
continue;

producerComponent.NextUpdate += producerComponent.UpdateInterval;

// for every production type the producer has
foreach (var productionType in Enumerable.Select(producerComponent.ProductionTypes, productionTypeId => _protoManager.Index(productionTypeId)))
{
// ensure there's a solution to add to
_solutionContainer.EnsureSolution(uid, productionType.SolutionName, out var solution, productionType.MaximumCapacity);

Check failure on line 67 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

No overload for method 'EnsureSolution' takes 4 arguments

Check failure on line 67 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 4 arguments

Check failure on line 67 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 4 arguments

Check failure on line 67 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 4 arguments

if (solution == null)

Check failure on line 69 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

The result of the expression is always 'false' since a value of type 'Entity<SolutionComponent>' is never equal to 'null' of type 'Entity<SolutionComponent>?'

Check failure on line 69 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The result of the expression is always 'false' since a value of type 'Entity<SolutionComponent>' is never equal to 'null' of type 'Entity<SolutionComponent>?'
continue;

// do some math to figure out how much we can add
var amountToAdd = FixedPoint2.Min(solution.AvailableVolume, productionType.UnitsPerProduction);

Check failure on line 73 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

'Entity<SolutionComponent>' does not contain a definition for 'AvailableVolume' and no accessible extension method 'AvailableVolume' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 73 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AvailableVolume' and no accessible extension method 'AvailableVolume' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 73 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AvailableVolume' and no accessible extension method 'AvailableVolume' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 73 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AvailableVolume' and no accessible extension method 'AvailableVolume' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

if (amountToAdd <= 0)
continue;
//and add it :)
solution.AddReagent(productionType.Reagent, amountToAdd);

Check failure on line 78 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

'Entity<SolutionComponent>' does not contain a definition for 'AddReagent' and no accessible extension method 'AddReagent' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 78 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AddReagent' and no accessible extension method 'AddReagent' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 78 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AddReagent' and no accessible extension method 'AddReagent' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 78 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'Entity<SolutionComponent>' does not contain a definition for 'AddReagent' and no accessible extension method 'AddReagent' accepting a first argument of type 'Entity<SolutionComponent>' could be found (are you missing a using directive or an assembly reference?)
}
}
}

private void AddVerbs(Entity<RefillableSolutionComponent> container, ref GetVerbsEvent<InteractionVerb> args)
{
var user = args.User;

if (!args.Using.HasValue || !args.CanInteract || !args.CanAccess)
return;

if (!TryComp<ReagentProducerComponent>(user, out var producerComp))
return;

// Add a verb for every production type the producer has
foreach (var productionTypeId in producerComp.ProductionTypes)
{
if (!_protoManager.TryIndex(productionTypeId, out var productionType) ||
!_protoManager.TryIndex(productionType.Reagent, out var reagent))
continue;

// I'd love if I could specify this via yaml. alas YOU CANT DEFINE SPRITES VIA -
var icon = productionType.NsfwVerbIcon
? new SpriteSpecifier.Texture(new ResPath("/Textures/_DEN/Interface/VerbIcons/lewd.svg.192dpi.png"))
: null;

var verb = new InteractionVerb
{
Category = ReagentFillCategory,
Act = () => StartFillDoAfter((user, producerComp), container, productionTypeId),
Text = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(reagent.LocalizedName),
Priority = -1,
CloseMenu = false,
Icon = icon,
};
args.Verbs.Add(verb);
}
}

private void StartFillDoAfter(
Entity<ReagentProducerComponent> user,
Entity<RefillableSolutionComponent> target,
ProtoId<ReagentProductionTypePrototype> productionTypeId
)
{
var productionType = _protoManager.Index(productionTypeId);

_doAfter.TryStartDoAfter(
new DoAfterArgs(EntityManager, user, productionType.DoAfterLength, new ReagentProductionFillEvent(productionTypeId), user, target: target)
{
BreakOnMove = true,
BreakOnDropItem = true,
});
}

private void FinishFillDoAfter(Entity<ReagentProducerComponent> ent, ref ReagentProductionFillEvent args)
{
if (!_protoManager.TryIndex(args.ProductionType, out var productionType) || args.Target == null || args.Cancelled || args.Handled)
return;

if (!TryComp<RefillableSolutionComponent>(args.Target.Value, out var refillableSolution))
return;

if (!_solutionContainer.TryGetSolution(ent.Owner, productionType.SolutionName, out var userSolutionComp)||
!_solutionContainer.TryGetSolution(args.Target.Value, refillableSolution.Solution, out var targetSolutionComp))
return;

var targetSolution = targetSolutionComp.Value.Comp.Solution;

// If there's no cum to cum you cant cum okay?
if (userSolutionComp.Value.Comp.Solution.Volume <= 0)
{
_popup.PopupPredicted(Loc.GetString(productionType.DryPopup),args.Args.User,args.Args.User);
return;
}

// Get available volume in target solution
var targetAvailableVolume = targetSolution.MaxVolume - targetSolution.Volume;

// If theres no room just silently return
if (targetAvailableVolume <= 0)
return;

// Get amount to add, attempts to add the largest amount with the maximum set from production type
var amountToAdd =
FixedPoint2.Clamp(targetAvailableVolume, FixedPoint2.Zero, productionType.MaximumLoad);

var split = _solutionContainer.SplitSolution(userSolutionComp.Value, amountToAdd);
var quantity = _solutionContainer.AddSolution(targetSolutionComp.Value, split);

_popup.PopupPredicted(
Loc.GetString(
productionType.SuccessPopup,
("amount", quantity),
("target", Identity.Entity(args.Target.Value, EntityManager))),
args.Args.User,
args.Args.User,
PopupType.Medium);

args.Handled = true;
}

/// <summary>
/// Raise an event on an entity to add the provided production type,
/// adds the <see cref="ReagentProducerComponent"/> if it does not exist.
/// </summary>
/// <param name="entity">Entity to add the production type to.</param>
/// <param name="production">The <see cref="ReagentProductionTypePrototype"/> to add.</param>
[PublicAPI]
public void AddProductionType(EntityUid entity, ReagentProductionTypePrototype production)
{
EnsureComp<ReagentProducerComponent>(entity);

RaiseLocalEvent(entity, new ReagentProductionTypeAdded(production));
}

/// <summary>
/// Raise an event on an entity to remove the provided production type,
/// removes the <see cref="ReagentProducerComponent"/> if there is no production type remaining.
/// </summary>
/// <param name="entity">Entity to remove the production type from.</param>
/// <param name="production">The <see cref="ReagentProductionTypePrototype"/> to remove.</param>
[PublicAPI]
public void RemoveProductionType(EntityUid entity, ReagentProductionTypePrototype production)
{
EnsureComp<ReagentProducerComponent>(entity);

RaiseLocalEvent(entity, new ReagentProductionTypeRemoved(production));
}

private void ProductionTypeAdded(Entity<ReagentProducerComponent> ent, ref ReagentProductionTypeAdded args)
{
if (!_protoManager.TryIndex(args.ProductionType, out var productionType))
return;

ent.Comp.ProductionTypes.Add(args.ProductionType);

_solutionContainer.EnsureSolution(ent.Owner, productionType.SolutionName,out _, out var solution, productionType.MaximumCapacity);

Check failure on line 216 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / YAML Linter

No overload for method 'EnsureSolution' takes 5 arguments

Check failure on line 216 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 5 arguments

Check failure on line 216 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 5 arguments

Check failure on line 216 in Content.Shared/_DEN/ReagentProduction/EntitySystems/ReagentProductionSystem.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

No overload for method 'EnsureSolution' takes 5 arguments
solution?.AddReagent(productionType.Reagent, productionType.MaximumCapacity);
}

private void ProductionTypeRemoved(Entity<ReagentProducerComponent> ent, ref ReagentProductionTypeRemoved args)
{
ent.Comp.ProductionTypes.Remove(args.ProductionType);
//If there are no more production types, just remove the component
if (ent.Comp.ProductionTypes.Count == 0)
RemCompDeferred<ReagentProducerComponent>(ent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Content.Shared._DEN.ReagentProduction.EntitySystems;
using Content.Shared._DEN.ReagentProduction.Prototypes;
using Content.Shared.DoAfter;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;


namespace Content.Shared._DEN.ReagentProduction.Events;

public sealed class ReagentProductionEvents
{
/// <summary>
/// When this event is called, a production type is added to the entity it's called on.
/// Do not call this directly, and instead use <see cref="ReagentProductionSystem.AddProductionType"/>
/// </summary>
/// <param name="productionType">Production type to add.</param>
[Serializable, NetSerializable,]
public sealed class ReagentProductionTypeAdded(ProtoId<ReagentProductionTypePrototype> productionType) : EntityEventArgs
Comment thread
honeyed-lemons marked this conversation as resolved.
{
public ProtoId<ReagentProductionTypePrototype> ProductionType { get; } = productionType;
}

/// <summary>
/// When this event is called, a production type is removed from the entity it's called on.
/// Do not call this directly, and instead use <see cref="ReagentProductionSystem.RemoveProductionType"/>
/// </summary>
/// <param name="productionType">Production type to remove.</param>
[Serializable, NetSerializable,]
public sealed class ReagentProductionTypeRemoved(ProtoId<ReagentProductionTypePrototype> productionType) : EntityEventArgs
Comment thread
honeyed-lemons marked this conversation as resolved.
{
public ProtoId<ReagentProductionTypePrototype> ProductionType { get; } = productionType;
}
}
/// <summary>
/// Classic doafter event, called when attempting to fill a solution container with a specific production type.
/// </summary>
[Serializable, NetSerializable,]
public sealed partial class ReagentProductionFillEvent : DoAfterEvent
Comment thread
honeyed-lemons marked this conversation as resolved.
{
/// <summary>
/// Production type to fill with.
/// </summary>
public ProtoId<ReagentProductionTypePrototype> ProductionType;

public ReagentProductionFillEvent( ProtoId<ReagentProductionTypePrototype> productionType)
{
ProductionType = productionType;
}

public override DoAfterEvent Clone()
{
return this;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

namespace Content.Shared._DEN.ReagentProduction.Prototypes;

[Prototype]
public sealed partial class ReagentProductionTypePrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;

/// <summary>
/// Reagent to produce
/// </summary>
[DataField]
public ProtoId<ReagentPrototype> Reagent = "Water";
/// <summary>
/// The solution to produce into
/// </summary>
[DataField]
public string SolutionName = "balls"; //cum is stored in the balls?
/// <summary>
/// Maximum capacity of the solution
/// </summary>
[DataField]
public FixedPoint2 MaximumCapacity = 30;

/// <summary>
/// Maximum amount you can expel at once
/// </summary>
[DataField]
public FixedPoint2 MaximumLoad = 10;

[DataField]
public TimeSpan DoAfterLength = TimeSpan.FromSeconds(3);

/// <summary>
/// How many units are produced each update
/// </summary>
[DataField]
public FixedPoint2 UnitsPerProduction = 5;

/// <summary>
/// Determines if the verb icon is NSFW or not.. I'd love to specify an actual texture here but YOU CANT SPECIFY SPECIFIC TEXTURES IN YAML !!!!!!!!!!
/// </summary>
[DataField]
public bool NsfwVerbIcon;

/// <summary>
/// Popup that occurs when your solution is empty
/// </summary>
[DataField]
public string DryPopup = "cum-verb-dry";

[DataField]
public string SuccessPopup = "cum-verb-success";
}
Loading
Loading