using Content.Shared.Storage;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Animals.Components;
/// This component handles animals which lay eggs (or some other item) on a timer, using up hunger to do so.
/// It also grants an action to players who are controlling these entities, allowing them to do it manually.
/// </summary>
+
[RegisterComponent]
public sealed partial class EggLayerComponent : Component
{
- [DataField("eggLayAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string EggLayAction = "ActionAnimalLayEgg";
+ [DataField]
+ public EntProtoId EggLayAction = "ActionAnimalLayEgg";
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("hungerUsage")]
+ /// <summary>
+ /// The amount of nutrient consumed on update.
+ /// </summary>
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
public float HungerUsage = 60f;
/// <summary>
/// Minimum cooldown used for the automatic egg laying.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("eggLayCooldownMin")]
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
public float EggLayCooldownMin = 60f;
/// <summary>
/// Maximum cooldown used for the automatic egg laying.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("eggLayCooldownMax")]
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
public float EggLayCooldownMax = 120f;
/// <summary>
[ViewVariables(VVAccess.ReadWrite)]
public float CurrentEggLayCooldown;
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("eggSpawn", required: true)]
+ [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
public List<EntitySpawnEntry> EggSpawn = default!;
- [DataField("eggLaySound")]
+ [DataField]
public SoundSpecifier EggLaySound = new SoundPathSpecifier("/Audio/Effects/pop.ogg");
- [DataField("accumulatedFrametime")]
+ [DataField]
public float AccumulatedFrametime;
[DataField] public EntityUid? Action;
using Content.Server.Animals.Systems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+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>
- [ViewVariables(VVAccess.ReadOnly)]
- [DataField("reagentId", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
- public string ReagentId = "Milk";
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public ProtoId<ReagentPrototype> ReagentId = "Milk";
/// <summary>
/// The solution to add reagent to.
/// </summary>
- [ViewVariables(VVAccess.ReadOnly)]
- [DataField("targetSolution")]
- public string TargetSolutionName = "udder";
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public string Solution = "udder";
/// <summary>
/// The amount of reagent to be generated on update.
/// </summary>
- [ViewVariables(VVAccess.ReadOnly)]
- [DataField("quantity")]
- public FixedPoint2 QuantityPerUpdate = 1;
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public FixedPoint2 QuantityPerUpdate = 25;
/// <summary>
- /// The time between updates (in seconds).
+ /// The amount of nutrient consumed on update.
/// </summary>
- [ViewVariables(VVAccess.ReadOnly)]
- [DataField("updateRate")]
- public float UpdateRate = 5;
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public float HungerUsage = 10f;
- public float AccumulatedFrameTime;
+ /// <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);
}
}
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+namespace Content.Server.Animals.Components;
+
/// <summary>
-/// Lets an animal grow a wool solution when not hungry.
+/// Lets an entity produce wool fibers. Uses hunger if present.
/// </summary>
+
[RegisterComponent, Access(typeof(WoolySystem))]
public sealed partial class WoolyComponent : Component
{
/// <summary>
- /// What reagent to grow.
+ /// The reagent to grow.
/// </summary>
- [DataField, ViewVariables(VVAccess.ReadWrite)]
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
public ProtoId<ReagentPrototype> ReagentId = "Fiber";
/// <summary>
- /// How much wool to grow at every growth cycle.
+ /// The solution to add reagent to.
/// </summary>
- [DataField, ViewVariables(VVAccess.ReadWrite)]
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
+ public string Solution = "wool";
+
+ /// <summary>
+ /// The amount of reagent to be generated on update.
+ /// </summary>
+ [DataField, ViewVariables(VVAccess.ReadOnly)]
public FixedPoint2 Quantity = 25;
/// <summary>
- /// What solution to add the wool reagent to.
+ /// The amount of nutrient consumed on update.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
- public string Solution = "wool";
+ public float HungerUsage = 10f;
/// <summary>
- /// How long to wait before growing wool.
+ /// How long to wait before growing wool.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan GrowthDelay = TimeSpan.FromMinutes(1);
/// <summary>
- /// When to next try growing wool.
+ /// When to next try growing wool.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan NextGrowth = TimeSpan.FromSeconds(0);
using Content.Server.Animals.Components;
using Content.Server.Popups;
using Content.Shared.Actions.Events;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Storage;
namespace Content.Server.Animals.Systems;
+/// <summary>
+/// Gives ability to produce eggs, produces endless if the
+/// owner has no HungerComponent
+/// </summary>
public sealed class EggLayerSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly HungerSystem _hunger = default!;
[Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
public override void Initialize()
{
component.CurrentEggLayCooldown = _random.NextFloat(component.EggLayCooldownMin, component.EggLayCooldownMax);
}
- private void OnEggLayAction(EntityUid uid, EggLayerComponent component, EggLayInstantActionEvent args)
+ private void OnEggLayAction(EntityUid uid, EggLayerComponent egglayer, EggLayInstantActionEvent args)
{
- args.Handled = TryLayEgg(uid, component);
+ args.Handled = TryLayEgg(uid, egglayer);
}
- public bool TryLayEgg(EntityUid uid, EggLayerComponent? component)
+ public bool TryLayEgg(EntityUid uid, EggLayerComponent? egglayer)
{
- if (!Resolve(uid, ref component))
+ if (!Resolve(uid, ref egglayer))
+ return false;
+
+ if (_mobState.IsDead(uid))
return false;
// Allow infinitely laying eggs if they can't get hungry
if (TryComp<HungerComponent>(uid, out var hunger))
{
- if (hunger.CurrentHunger < component.HungerUsage)
+ if (hunger.CurrentHunger < egglayer.HungerUsage)
{
_popup.PopupEntity(Loc.GetString("action-popup-lay-egg-too-hungry"), uid, uid);
return false;
}
- _hunger.ModifyHunger(uid, -component.HungerUsage, hunger);
+ _hunger.ModifyHunger(uid, -egglayer.HungerUsage, hunger);
}
- foreach (var ent in EntitySpawnCollection.GetSpawns(component.EggSpawn, _random))
+ foreach (var ent in EntitySpawnCollection.GetSpawns(egglayer.EggSpawn, _random))
{
Spawn(ent, Transform(uid).Coordinates);
}
// Sound + popups
- _audio.PlayPvs(component.EggLaySound, uid);
+ _audio.PlayPvs(egglayer.EggLaySound, uid);
_popup.PopupEntity(Loc.GetString("action-popup-lay-egg-user"), uid, uid);
_popup.PopupEntity(Loc.GetString("action-popup-lay-egg-others", ("entity", uid)), uid, Filter.PvsExcept(uid), true);
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Udder;
using Content.Shared.Verbs;
+using Robust.Shared.Timing;
-namespace Content.Server.Animals.Systems
+namespace Content.Server.Animals.Systems;
+
+/// <summary>
+/// Gives ability to produce milkable reagents, produces endless if the
+/// owner has no HungerComponent
+/// </summary>
+internal sealed class UdderSystem : EntitySystem
{
- /// <summary>
- /// Gives ability to living beings with acceptable hunger level to produce milkable reagents.
- /// </summary>
- internal sealed class UdderSystem : EntitySystem
+ [Dependency] private readonly HungerSystem _hunger = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
+ [Dependency] private readonly PopupSystem _popupSystem = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+ [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+
+ public override void Initialize()
{
- [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
- [Dependency] private readonly HungerSystem _hunger = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
+ base.Initialize();
- public override void Initialize()
- {
- base.Initialize();
+ SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
+ SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
+ }
- SubscribeLocalEvent<UdderComponent, GetVerbsEvent<AlternativeVerb>>(AddMilkVerb);
- SubscribeLocalEvent<UdderComponent, MilkingDoAfterEvent>(OnDoAfter);
- }
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
- public override void Update(float frameTime)
+ var query = EntityQueryEnumerator<UdderComponent>();
+ var now = _timing.CurTime;
+ while (query.MoveNext(out var uid, out var udder))
{
- var query = EntityQueryEnumerator<UdderComponent>();
- while (query.MoveNext(out var uid, out var udder))
+ if (now < udder.NextGrowth)
+ continue;
+
+ udder.NextGrowth = now + udder.GrowthDelay;
+
+ if (_mobState.IsDead(uid))
+ continue;
+
+ // Actually there is food digestion so no problem with instant reagent generation "OnFeed"
+ if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
{
- udder.AccumulatedFrameTime += frameTime;
-
- while (udder.AccumulatedFrameTime > udder.UpdateRate)
- {
- udder.AccumulatedFrameTime -= udder.UpdateRate;
-
- // Actually there is food digestion so no problem with instant reagent generation "OnFeed"
- if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
- {
- // Is there enough nutrition to produce reagent?
- if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Peckish)
- continue;
- }
-
- if (!_solutionContainerSystem.TryGetSolution(uid, udder.TargetSolutionName,
- out var solution))
- continue;
-
- //TODO: toxins from bloodstream !?
- _solutionContainerSystem.TryAddReagent(uid, solution, udder.ReagentId,
- udder.QuantityPerUpdate, out var accepted);
- }
+ // Is there enough nutrition to produce reagent?
+ if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)
+ continue;
+
+ _hunger.ModifyHunger(uid, -udder.HungerUsage, hunger);
}
+
+ if (!_solutionContainerSystem.TryGetSolution(uid, udder.Solution, out var solution))
+ continue;
+
+ //TODO: toxins from bloodstream !?
+ _solutionContainerSystem.TryAddReagent(uid, solution, udder.ReagentId, udder.QuantityPerUpdate, out _);
}
+ }
+
+ private void AttemptMilk(Entity<UdderComponent?> udder, EntityUid userUid, EntityUid containerUid)
+ {
+ if (!Resolve(udder, ref udder.Comp))
+ return;
- private void AttemptMilk(EntityUid uid, EntityUid userUid, EntityUid containerUid, UdderComponent? udder = null)
+ var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), udder, udder, used: containerUid)
{
- if (!Resolve(uid, ref udder))
- return;
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ MovementThreshold = 1.0f,
+ };
- var doargs = new DoAfterArgs(EntityManager, userUid, 5, new MilkingDoAfterEvent(), uid, uid, used: containerUid)
- {
- BreakOnUserMove = true,
- BreakOnDamage = true,
- BreakOnTargetMove = true,
- MovementThreshold = 1.0f,
- };
+ _doAfterSystem.TryStartDoAfter(doargs);
+ }
- _doAfterSystem.TryStartDoAfter(doargs);
- }
+ private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
+ {
+ if (args.Cancelled || args.Handled || args.Args.Used == null)
+ return;
- private void OnDoAfter(EntityUid uid, UdderComponent component, MilkingDoAfterEvent args)
- {
- if (args.Cancelled || args.Handled || args.Args.Used == null)
- return;
+ if (!_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution))
+ return;
- if (!_solutionContainerSystem.TryGetSolution(uid, component.TargetSolutionName, out var solution))
- return;
+ if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
+ return;
- if (!_solutionContainerSystem.TryGetRefillableSolution(args.Args.Used.Value, out var targetSolution))
- return;
+ args.Handled = true;
+ var quantity = solution.Volume;
+ if (quantity == 0)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
+ return;
+ }
- args.Handled = true;
- var quantity = solution.Volume;
- if(quantity == 0)
- {
- _popupSystem.PopupEntity(Loc.GetString("udder-system-dry"), uid, args.Args.User);
- return;
- }
+ if (quantity > targetSolution.AvailableVolume)
+ quantity = targetSolution.AvailableVolume;
- if (quantity > targetSolution.AvailableVolume)
- quantity = targetSolution.AvailableVolume;
+ var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
+ _solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
- var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
- _solutionContainerSystem.TryAddSolution(args.Args.Used.Value, targetSolution, split);
+ _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
+ args.Args.User, PopupType.Medium);
+ }
- _popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(args.Args.Used.Value, EntityManager))), uid,
- args.Args.User, PopupType.Medium);
- }
+ private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
+ {
+ if (args.Using == null ||
+ !args.CanInteract ||
+ !EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
+ return;
- private void AddMilkVerb(EntityUid uid, UdderComponent component, GetVerbsEvent<AlternativeVerb> args)
+ AlternativeVerb verb = new()
{
- if (args.Using == null ||
- !args.CanInteract ||
- !EntityManager.HasComponent<RefillableSolutionComponent>(args.Using.Value))
- return;
-
- AlternativeVerb verb = new()
+ Act = () =>
{
- Act = () =>
- {
- AttemptMilk(uid, args.User, args.Using.Value, component);
- },
- Text = Loc.GetString("udder-system-verb-milk"),
- Priority = 2
- };
- args.Verbs.Add(verb);
- }
+ AttemptMilk(uid, args.User, args.Using.Value);
+ },
+ Text = Loc.GetString("udder-system-verb-milk"),
+ Priority = 2
+ };
+ args.Verbs.Add(verb);
}
}
using Content.Server.Animals.Components;
using Content.Server.Nutrition;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Timing;
namespace Content.Server.Animals.Systems;
/// <summary>
-/// Handles regeneration of an animal's wool solution when not hungry.
-/// Shearing is not currently possible so the only use is for moths to eat.
+/// Gives ability to produce fiber reagents, produces endless if the
+/// owner has no HungerComponent
/// </summary>
public sealed class WoolySystem : EntitySystem
{
[Dependency] private readonly HungerSystem _hunger = default!;
[Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
public override void Initialize()
{
base.Update(frameTime);
- var query = EntityQueryEnumerator<WoolyComponent, HungerComponent>();
+ var query = EntityQueryEnumerator<WoolyComponent>();
var now = _timing.CurTime;
- while (query.MoveNext(out var uid, out var comp, out var hunger))
+ while (query.MoveNext(out var uid, out var wooly))
{
- if (now < comp.NextGrowth)
+ if (now < wooly.NextGrowth)
continue;
- comp.NextGrowth = now + comp.GrowthDelay;
+ wooly.NextGrowth = now + wooly.GrowthDelay;
- // Is there enough nutrition to produce reagent?
- if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Peckish)
+ if (_mobState.IsDead(uid))
continue;
- if (!_solutionContainer.TryGetSolution(uid, comp.Solution, out var solution))
+ // Actually there is food digestion so no problem with instant reagent generation "OnFeed"
+ if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger))
+ {
+ // Is there enough nutrition to produce reagent?
+ if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay)
+ continue;
+
+ _hunger.ModifyHunger(uid, -wooly.HungerUsage, hunger);
+ }
+
+ if (!_solutionContainer.TryGetSolution(uid, wooly.Solution, out var solution))
continue;
- _solutionContainer.TryAddReagent(uid, solution, comp.ReagentId, comp.Quantity, out _);
+ _solutionContainer.TryAddReagent(uid, solution, wooly.ReagentId, wooly.Quantity, out _);
}
}
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Systems;
+using Content.Shared.Nutrition.AnimalHusbandry;
using Content.Shared.Nutrition.Components;
using Content.Shared.Popups;
using Content.Shared.Roles;
var zombiecomp = AddComp<ZombieComponent>(target);
//we need to basically remove all of these because zombies shouldn't
- //get diseases, breath, be thirst, be hungry, or die in space
+ //get diseases, breath, be thirst, be hungry, die in space or have offspring
RemComp<RespiratorComponent>(target);
RemComp<BarotraumaComponent>(target);
RemComp<HungerComponent>(target);
RemComp<ThirstComponent>(target);
+ RemComp<ReproductiveComponent>(target);
+ RemComp<ReproductivePartnerComponent>(target);
//funny voice
var accentType = "zombie";