Fix udder wooly reagent creation V2 (#32905)
* Changed comments to be more clear and uniform. EggLayer uses NextGrowth instead of frame accumulation. Egglayer uses much less energy to make eggs, and lay time is randomized for player and AI chicken. * UdderComponent ReagentId can be changed now UdderSystem and WoolySystem use SharedSolutionContainerSystem now * Entities with udders can be examined to see a rough hunger level udders and wooly stop reagent generation/extra nutrient usage once the solution container is full * Moved stuff to Shared AutoPausedField now * Cleanup moving stuff to Shared * Oops. Make UdderSystem sealed instead of abstract. * Switch PopupEntity for PopupClient * Didn't mean to delete Access * new() instead of default! prototype revert egglayer balance change NextGrowth += timespan in egglayer * forgot [Datafield] for NextGrowth * forgot NetworkedComponent again... * Renaming Shared Animal to Shared Animals to match Server Hopefully also resolve merge conflicts. * Fix incorrect filename * Update with requested changes Put UdderSystem dependencies in alphabetical order. Initialise NextGrowth for Udder and Wooly components on MapInitEvent. Clean-up EggLayerSystem a little. Re-write OnExamine function for UdderSystem, improving clarity. Add full stops to end of udder examine locales. And more :) * Add some additional descriptions for cow hunger levels. * Add Udder and Wooly quantity to AutoNetworkedField * Account for less than starving threshold. --------- Co-authored-by: sirionaut <sirionaut@gmail.com> Co-authored-by: Sirionaut <148076704+Sirionaut@users.noreply.github.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Animals.Systems;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -9,44 +10,47 @@ namespace Content.Server.Animals.Components;
|
|||||||
/// It also grants an action to players who are controlling these entities, allowing them to do it manually.
|
/// It also grants an action to players who are controlling these entities, allowing them to do it manually.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent, Access(typeof(EggLayerSystem)), AutoGenerateComponentPause]
|
||||||
public sealed partial class EggLayerComponent : Component
|
public sealed partial class EggLayerComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The item that gets laid/spawned, retrieved from animal prototype.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public List<EntitySpawnEntry> EggSpawn = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Player action.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public EntProtoId EggLayAction = "ActionAnimalLayEgg";
|
public EntProtoId EggLayAction = "ActionAnimalLayEgg";
|
||||||
|
|
||||||
/// <summary>
|
[DataField]
|
||||||
/// The amount of nutrient consumed on update.
|
public SoundSpecifier EggLaySound = new SoundPathSpecifier("/Audio/Effects/pop.ogg");
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float HungerUsage = 60f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum cooldown used for the automatic egg laying.
|
/// Minimum cooldown used for the automatic egg laying.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float EggLayCooldownMin = 60f;
|
public float EggLayCooldownMin = 60f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum cooldown used for the automatic egg laying.
|
/// Maximum cooldown used for the automatic egg laying.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float EggLayCooldownMax = 120f;
|
public float EggLayCooldownMax = 120f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set during component init.
|
/// The amount of nutrient consumed on update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float CurrentEggLayCooldown;
|
|
||||||
|
|
||||||
[DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public List<EntitySpawnEntry> EggSpawn = default!;
|
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public SoundSpecifier EggLaySound = new SoundPathSpecifier("/Audio/Effects/pop.ogg");
|
public float HungerUsage = 60f;
|
||||||
|
|
||||||
[DataField]
|
|
||||||
public float AccumulatedFrametime;
|
|
||||||
|
|
||||||
[DataField] public EntityUid? Action;
|
[DataField] public EntityUid? Action;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When to next try to produce.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoPausedField]
|
||||||
|
public TimeSpan NextGrowth = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
using Content.Server.Animals.Systems;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
|
||||||
using Content.Shared.Chemistry.Reagent;
|
|
||||||
using Content.Shared.FixedPoint;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
|
||||||
|
|
||||||
namespace Content.Server.Animals.Components
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lets an entity produce milk. Uses hunger if present.
|
|
||||||
/// </summary>
|
|
||||||
{
|
|
||||||
[RegisterComponent, Access(typeof(UdderSystem))]
|
|
||||||
internal sealed partial class UdderComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The reagent to produce.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public ProtoId<ReagentPrototype> ReagentId = "Milk";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of <see cref="Solution"/>.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public string SolutionName = "udder";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The solution to add reagent to.
|
|
||||||
/// </summary>
|
|
||||||
[DataField]
|
|
||||||
public Entity<SolutionComponent>? Solution = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of reagent to be generated on update.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public FixedPoint2 QuantityPerUpdate = 25;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of nutrient consumed on update.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float HungerUsage = 10f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long to wait before producing.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When to next try to produce.
|
|
||||||
/// </summary>
|
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan NextGrowth = TimeSpan.FromSeconds(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,15 +7,15 @@ using Content.Shared.Nutrition.Components;
|
|||||||
using Content.Shared.Nutrition.EntitySystems;
|
using Content.Shared.Nutrition.EntitySystems;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Server.Audio;
|
using Robust.Server.Audio;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Animals.Systems;
|
namespace Content.Server.Animals.Systems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gives ability to produce eggs, produces endless if the
|
/// Gives the ability to lay eggs/other things;
|
||||||
/// owner has no HungerComponent
|
/// produces endlessly if the owner does not have a HungerComponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EggLayerSystem : EntitySystem
|
public sealed class EggLayerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -23,6 +23,7 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
|
|
||||||
@@ -37,7 +38,6 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<EggLayerComponent>();
|
var query = EntityQueryEnumerator<EggLayerComponent>();
|
||||||
while (query.MoveNext(out var uid, out var eggLayer))
|
while (query.MoveNext(out var uid, out var eggLayer))
|
||||||
{
|
{
|
||||||
@@ -45,13 +45,17 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
if (HasComp<ActorComponent>(uid))
|
if (HasComp<ActorComponent>(uid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
eggLayer.AccumulatedFrametime += frameTime;
|
if (_timing.CurTime < eggLayer.NextGrowth)
|
||||||
|
|
||||||
if (eggLayer.AccumulatedFrametime < eggLayer.CurrentEggLayCooldown)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
eggLayer.AccumulatedFrametime -= eggLayer.CurrentEggLayCooldown;
|
// Randomize next growth time for more organic egglaying.
|
||||||
eggLayer.CurrentEggLayCooldown = _random.NextFloat(eggLayer.EggLayCooldownMin, eggLayer.EggLayCooldownMax);
|
eggLayer.NextGrowth += TimeSpan.FromSeconds(_random.NextFloat(eggLayer.EggLayCooldownMin, eggLayer.EggLayCooldownMax));
|
||||||
|
|
||||||
|
if (_mobState.IsDead(uid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Hungerlevel check/modification is done in TryLayEgg()
|
||||||
|
// so it's used for player controlled chickens as well.
|
||||||
|
|
||||||
TryLayEgg(uid, eggLayer);
|
TryLayEgg(uid, eggLayer);
|
||||||
}
|
}
|
||||||
@@ -60,11 +64,12 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
private void OnMapInit(EntityUid uid, EggLayerComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, EggLayerComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
_actions.AddAction(uid, ref component.Action, component.EggLayAction);
|
_actions.AddAction(uid, ref component.Action, component.EggLayAction);
|
||||||
component.CurrentEggLayCooldown = _random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax);
|
component.NextGrowth = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEggLayAction(EntityUid uid, EggLayerComponent egglayer, EggLayInstantActionEvent args)
|
private void OnEggLayAction(EntityUid uid, EggLayerComponent egglayer, EggLayInstantActionEvent args)
|
||||||
{
|
{
|
||||||
|
// Cooldown is handeled by ActionAnimalLayEgg in types.yml.
|
||||||
args.Handled = TryLayEgg(uid, egglayer);
|
args.Handled = TryLayEgg(uid, egglayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +81,7 @@ public sealed class EggLayerSystem : EntitySystem
|
|||||||
if (_mobState.IsDead(uid))
|
if (_mobState.IsDead(uid))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Allow infinitely laying eggs if they can't get hungry
|
// Allow infinitely laying eggs if they can't get hungry.
|
||||||
if (TryComp<HungerComponent>(uid, out var hunger))
|
if (TryComp<HungerComponent>(uid, out var hunger))
|
||||||
{
|
{
|
||||||
if (hunger.CurrentHunger < egglayer.HungerUsage)
|
if (hunger.CurrentHunger < egglayer.HungerUsage)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Actions;
|
|||||||
using Content.Server.Humanoid;
|
using Content.Server.Humanoid;
|
||||||
using Content.Server.Inventory;
|
using Content.Server.Inventory;
|
||||||
using Content.Server.Mind.Commands;
|
using Content.Server.Mind.Commands;
|
||||||
using Content.Server.Nutrition;
|
|
||||||
using Content.Server.Polymorph.Components;
|
using Content.Server.Polymorph.Components;
|
||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Buckle;
|
using Content.Shared.Buckle;
|
||||||
@@ -13,6 +12,7 @@ using Content.Shared.IdentityManagement;
|
|||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Nutrition;
|
||||||
using Content.Shared.Polymorph;
|
using Content.Shared.Polymorph;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Server.Audio;
|
using Robust.Server.Audio;
|
||||||
|
|||||||
57
Content.Shared/Animals/UdderComponent.cs
Normal file
57
Content.Shared/Animals/UdderComponent.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Animals;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gives the ability to produce a solution;
|
||||||
|
/// produces endlessly if the owner does not have a HungerComponent.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, AutoGenerateComponentState, AutoGenerateComponentPause, NetworkedComponent]
|
||||||
|
public sealed partial class UdderComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The reagent to produce.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public ProtoId<ReagentPrototype> ReagentId = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of <see cref="Solution"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string SolutionName = "udder";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The solution to add reagent to.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public Entity<SolutionComponent>? Solution = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of reagent to be generated on update.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public FixedPoint2 QuantityPerUpdate = 25;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of nutrient consumed on update.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public float HungerUsage = 10f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long to wait before producing.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When to next try to produce.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoPausedField, Access(typeof(UdderSystem))]
|
||||||
|
public TimeSpan NextGrowth = TimeSpan.Zero;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
using Content.Server.Animals.Components;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Nutrition.Components;
|
using Content.Shared.Nutrition.Components;
|
||||||
@@ -12,18 +11,17 @@ using Content.Shared.Udder;
|
|||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Animals.Systems;
|
namespace Content.Shared.Animals;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gives ability to produce milkable reagents, produces endless if the
|
/// Gives the ability to produce milkable reagents;
|
||||||
/// owner has no HungerComponent
|
/// produces endlessly if the owner does not have a HungerComponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class UdderSystem : EntitySystem
|
public sealed class UdderSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
|
|
||||||
@@ -31,26 +29,37 @@ internal sealed class UdderSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<UdderComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
|
SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
|
||||||
SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
|
||||||
|
SubscribeLocalEvent<UdderComponent, ExaminedEvent>(OnExamine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(EntityUid uid, UdderComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
component.NextGrowth = _timing.CurTime + component.GrowthDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<UdderComponent>();
|
var query = EntityQueryEnumerator<UdderComponent>();
|
||||||
var now = _timing.CurTime;
|
|
||||||
while (query.MoveNext(out var uid, out var udder))
|
while (query.MoveNext(out var uid, out var udder))
|
||||||
{
|
{
|
||||||
if (now < udder.NextGrowth)
|
if (_timing.CurTime < udder.NextGrowth)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
udder.NextGrowth = now + udder.GrowthDelay;
|
udder.NextGrowth += udder.GrowthDelay;
|
||||||
|
|
||||||
if (_mobState.IsDead(uid))
|
if (_mobState.IsDead(uid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution, out var solution))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (solution.AvailableVolume == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Actually there is food digestion so no problem with instant reagent generation "OnFeed"
|
// Actually there is food digestion so no problem with instant reagent generation "OnFeed"
|
||||||
if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
|
if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
|
||||||
{
|
{
|
||||||
@@ -61,9 +70,6 @@ internal sealed class UdderSystem : EntitySystem
|
|||||||
_hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
|
_hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_solutionContainerSystem.ResolveSolution(uid, udder.SolutionName, ref udder.Solution))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
//TODO: toxins from bloodstream !?
|
//TODO: toxins from bloodstream !?
|
||||||
_solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
|
_solutionContainerSystem.TryAddReagent(udder.Solution.Value, udder.ReagentId, udder.QuantityPerUpdate, out _);
|
||||||
}
|
}
|
||||||
@@ -99,7 +105,7 @@ internal sealed class UdderSystem : EntitySystem
|
|||||||
var quantity = solution.Volume;
|
var quantity = solution.Volume;
|
||||||
if (quantity == 0)
|
if (quantity == 0)
|
||||||
{
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
|
_popupSystem.PopupClient(Loc.GetString("udder-system-dry"), entity.Owner, args.Args.User);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +115,7 @@ internal sealed class UdderSystem : EntitySystem
|
|||||||
var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
|
var split = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, quantity);
|
||||||
_solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
|
_solutionContainerSystem.TryAddSolution(targetSoln.Value, split);
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
|
_popupSystem.PopupClient(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), entity.Owner,
|
||||||
args.Args.User, PopupType.Medium);
|
args.Args.User, PopupType.Medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,4 +140,50 @@ internal sealed class UdderSystem : EntitySystem
|
|||||||
};
|
};
|
||||||
args.Verbs.Add(verb);
|
args.Verbs.Add(verb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the text provided on examine.
|
||||||
|
/// Changes depending on the amount of hunger the target has.
|
||||||
|
/// </summary>
|
||||||
|
private void OnExamine(Entity<UdderComponent> entity, ref ExaminedEvent args)
|
||||||
|
{
|
||||||
|
|
||||||
|
var entityIdentity = Identity.Entity(args.Examined, EntityManager);
|
||||||
|
|
||||||
|
string message;
|
||||||
|
|
||||||
|
// Check if the target has hunger, otherwise return not hungry.
|
||||||
|
if (!TryComp<HungerComponent>(entity, out var hunger))
|
||||||
|
{
|
||||||
|
message = Loc.GetString("udder-system-examine-none", ("entity", entityIdentity));
|
||||||
|
args.PushMarkup(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose the correct examine string based on HungerThreshold.
|
||||||
|
switch (_hunger.GetHungerThreshold(hunger))
|
||||||
|
{
|
||||||
|
case >= HungerThreshold.Overfed:
|
||||||
|
message = Loc.GetString("udder-system-examine-overfed", ("entity", entityIdentity));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HungerThreshold.Okay:
|
||||||
|
message = Loc.GetString("udder-system-examine-okay", ("entity", entityIdentity));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HungerThreshold.Peckish:
|
||||||
|
message = Loc.GetString("udder-system-examine-hungry", ("entity", entityIdentity));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// There's a final hunger threshold called "dead" but animals don't actually die so we'll re-use this.
|
||||||
|
case <= HungerThreshold.Starving:
|
||||||
|
message = Loc.GetString("udder-system-examine-starved", ("entity", entityIdentity));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.PushMarkup(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
using Content.Server.Animals.Systems;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
|
||||||
|
|
||||||
namespace Content.Server.Animals.Components;
|
namespace Content.Shared.Animals;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lets an entity produce wool fibers. Uses hunger if present.
|
/// Gives the ability to produce wool fibers;
|
||||||
|
/// produces endlessly if the owner does not have a HungerComponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[RegisterComponent, AutoGenerateComponentState, AutoGenerateComponentPause, NetworkedComponent]
|
||||||
[RegisterComponent, Access(typeof(WoolySystem))]
|
|
||||||
public sealed partial class WoolyComponent : Component
|
public sealed partial class WoolyComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The reagent to grow.
|
/// The reagent to grow.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
[DataField, AutoNetworkedField]
|
||||||
public ProtoId<ReagentPrototype> ReagentId = "Fiber";
|
public ProtoId<ReagentPrototype> ReagentId = "Fiber";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -29,30 +28,30 @@ public sealed partial class WoolyComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The solution to add reagent to.
|
/// The solution to add reagent to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||||
public Entity<SolutionComponent>? Solution;
|
public Entity<SolutionComponent>? Solution;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of reagent to be generated on update.
|
/// The amount of reagent to be generated on update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
[DataField, AutoNetworkedField]
|
||||||
public FixedPoint2 Quantity = 25;
|
public FixedPoint2 Quantity = 25;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of nutrient consumed on update.
|
/// The amount of nutrient consumed on update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public float HungerUsage = 10f;
|
public float HungerUsage = 10f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How long to wait before growing wool.
|
/// How long to wait before growing wool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
|
public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When to next try growing wool.
|
/// When to next try growing wool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoPausedField, Access(typeof(WoolySystem))]
|
||||||
public TimeSpan NextGrowth = TimeSpan.FromSeconds(0);
|
public TimeSpan NextGrowth = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
using Content.Server.Animals.Components;
|
|
||||||
using Content.Server.Nutrition;
|
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Nutrition;
|
||||||
using Content.Shared.Nutrition.Components;
|
using Content.Shared.Nutrition.Components;
|
||||||
using Content.Shared.Nutrition.EntitySystems;
|
using Content.Shared.Nutrition.EntitySystems;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Animals.Systems;
|
namespace Content.Shared.Animals;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gives ability to produce fiber reagents, produces endless if the
|
/// Gives ability to produce fiber reagents;
|
||||||
/// owner has no HungerComponent
|
/// produces endlessly if the owner has no HungerComponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class WoolySystem : EntitySystem
|
public sealed class WoolySystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -24,6 +23,12 @@ public sealed class WoolySystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<WoolyComponent, BeforeFullyEatenEvent>(OnBeforeFullyEaten);
|
SubscribeLocalEvent<WoolyComponent, BeforeFullyEatenEvent>(OnBeforeFullyEaten);
|
||||||
|
SubscribeLocalEvent<WoolyComponent, MapInitEvent>(OnMapInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(EntityUid uid, WoolyComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
component.NextGrowth = _timing.CurTime + component.GrowthDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
@@ -31,17 +36,22 @@ public sealed class WoolySystem : EntitySystem
|
|||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<WoolyComponent>();
|
var query = EntityQueryEnumerator<WoolyComponent>();
|
||||||
var now = _timing.CurTime;
|
|
||||||
while (query.MoveNext(out var uid, out var wooly))
|
while (query.MoveNext(out var uid, out var wooly))
|
||||||
{
|
{
|
||||||
if (now < wooly.NextGrowth)
|
if (_timing.CurTime < wooly.NextGrowth)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
wooly.NextGrowth = now + wooly.GrowthDelay;
|
wooly.NextGrowth += wooly.GrowthDelay;
|
||||||
|
|
||||||
if (_mobState.IsDead(uid))
|
if (_mobState.IsDead(uid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!_solutionContainer.ResolveSolution(uid, wooly.SolutionName, ref wooly.Solution, out var solution))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (solution.AvailableVolume == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Actually there is food digestion so no problem with instant reagent generation "OnFeed"
|
// Actually there is food digestion so no problem with instant reagent generation "OnFeed"
|
||||||
if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
|
if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
|
||||||
{
|
{
|
||||||
@@ -52,9 +62,6 @@ public sealed class WoolySystem : EntitySystem
|
|||||||
_hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
|
_hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_solutionContainer.ResolveSolution(uid, wooly.SolutionName, ref wooly.Solution))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
|
_solutionContainer.TryAddReagent(wooly.Solution.Value, wooly.ReagentId, wooly.Quantity, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Content.Server.Nutrition;
|
namespace Content.Shared.Nutrition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised directed at the consumer when attempting to ingest something.
|
/// Raised directed at the consumer when attempting to ingest something.
|
||||||
@@ -5,3 +5,9 @@ udder-system-success = You fill {THE($target)} with {$amount}u from the udder.
|
|||||||
udder-system-dry = The udder is dry.
|
udder-system-dry = The udder is dry.
|
||||||
|
|
||||||
udder-system-verb-milk = Milk
|
udder-system-verb-milk = Milk
|
||||||
|
|
||||||
|
udder-system-examine-overfed = {CAPITALIZE(SUBJECT($entity))} looks stuffed!
|
||||||
|
udder-system-examine-okay = {CAPITALIZE(SUBJECT($entity))} looks content.
|
||||||
|
udder-system-examine-hungry = {CAPITALIZE(SUBJECT($entity))} looks hungry.
|
||||||
|
udder-system-examine-starved = {CAPITALIZE(SUBJECT($entity))} looks starved!
|
||||||
|
udder-system-examine-none = {CAPITALIZE(SUBJECT($entity))} seems not to get hungry.
|
||||||
|
|||||||
Reference in New Issue
Block a user