diff --git a/Content.Client/Sericulture/SericultureSystem.cs b/Content.Client/Sericulture/SericultureSystem.cs new file mode 100644 index 0000000000..6b2ad2fd17 --- /dev/null +++ b/Content.Client/Sericulture/SericultureSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Sericulture; + +namespace Content.Client.Sericulture; + +/// +/// +/// +public sealed partial class SericultureSystem : SharedSericultureSystem { } diff --git a/Content.Server/Sericulture/SericultureComponent.cs b/Content.Server/Sericulture/SericultureComponent.cs deleted file mode 100644 index 2d258accc8..0000000000 --- a/Content.Server/Sericulture/SericultureComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Sericulture; - -[RegisterComponent] -public sealed partial class SericultureComponent : Component -{ - [DataField("popupText")] - public string PopupText = "sericulture-failure-hunger"; - - /// - /// What will be produced at the end of the action. - /// - [DataField("entityProduced", required: true)] - public string EntityProduced = ""; - - [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Action = "ActionSericulture"; - - [DataField("actionEntity")] public EntityUid? ActionEntity; - - /// - /// How long will it take to make. - /// - [DataField("productionLength", required: true), ViewVariables(VVAccess.ReadWrite)] - public float ProductionLength = 0; - - [DataField("hungerCost"), ViewVariables(VVAccess.ReadWrite)] - public float HungerCost = 0f; -} diff --git a/Content.Server/Sericulture/SericultureSystem.cs b/Content.Server/Sericulture/SericultureSystem.cs index c100d3d4a5..672c0b914b 100644 --- a/Content.Server/Sericulture/SericultureSystem.cs +++ b/Content.Server/Sericulture/SericultureSystem.cs @@ -1,89 +1,7 @@ -using Content.Server.Actions; -using Content.Server.DoAfter; -using Content.Server.Popups; -using Content.Server.Stack; -using Content.Shared.DoAfter; -using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Sericulture; namespace Content.Server.Sericulture; -public sealed partial class SericultureSystem : EntitySystem +public sealed partial class SericultureSystem : SharedSericultureSystem { - [Dependency] private readonly ActionsSystem _actionsSystem = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly HungerSystem _hungerSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly StackSystem _stackSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnCompMapInit); - SubscribeLocalEvent(OnCompRemove); - SubscribeLocalEvent(OnSericultureStart); - SubscribeLocalEvent(OnSericultureDoAfter); - } - - private void OnCompMapInit(EntityUid uid, SericultureComponent comp, MapInitEvent args) - { - _actionsSystem.AddAction(uid, ref comp.ActionEntity, comp.Action, uid); - } - - private void OnCompRemove(EntityUid uid, SericultureComponent comp, ComponentShutdown args) - { - _actionsSystem.RemoveAction(uid, comp.ActionEntity); - } - - private void OnSericultureStart(EntityUid uid, SericultureComponent comp, SericultureActionEvent args) - { - if (IsHungry(uid)) - { - _popupSystem.PopupEntity(Loc.GetString(comp.PopupText), uid, uid); - return; - } - - var doAfter = new DoAfterArgs(EntityManager, uid, comp.ProductionLength, new SericultureDoAfterEvent(), uid) - { - BreakOnUserMove = true, - BlockDuplicate = true, - BreakOnDamage = true, - CancelDuplicate = true, - }; - - _doAfterSystem.TryStartDoAfter(doAfter); - } - - private void OnSericultureDoAfter(EntityUid uid, SericultureComponent comp, SericultureDoAfterEvent args) - { - if (args.Cancelled || args.Handled || comp.Deleted) - return; - - if (IsHungry(uid)) - { - _popupSystem.PopupEntity(Loc.GetString(comp.PopupText), uid, uid); - return; - } - - _hungerSystem.ModifyHunger(uid, -comp.HungerCost); - - var newEntity = Spawn(comp.EntityProduced, Transform(uid).Coordinates); - - _stackSystem.TryMergeToHands(newEntity, uid); - - args.Repeat = true; - } - - private bool IsHungry(EntityUid uid, HungerComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return false; - - if (_hungerSystem.GetHungerThreshold(comp) <= HungerThreshold.Peckish) - return true; - - return false; - } } diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs index 8baee3e379..9c09603510 100644 --- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs @@ -173,6 +173,17 @@ public sealed class HungerSystem : EntitySystem return result; } + /// + /// A check that returns if the entity is below a hunger threshold. + /// + public bool IsHungerBelowState(EntityUid uid, HungerThreshold threshold, float? food = null, HungerComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return false; // It's never going to go hungry, so it's probably fine to assume that it's not... you know, hungry. + + return GetHungerThreshold(comp, food) < threshold; + } + private bool GetMovementThreshold(HungerThreshold threshold) { switch (threshold) diff --git a/Content.Shared/Sericulture/SericultureComponent.cs b/Content.Shared/Sericulture/SericultureComponent.cs new file mode 100644 index 0000000000..23143f5ac4 --- /dev/null +++ b/Content.Shared/Sericulture/SericultureComponent.cs @@ -0,0 +1,66 @@ +using Content.Shared.Nutrition.Components; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Sericulture; + +/// +/// Should be applied to any mob that you want to be able to produce any material with an action and the cost of hunger. +/// TODO: Probably adjust this to utilize organs? +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedSericultureSystem))] +public sealed partial class SericultureComponent : Component +{ + /// + /// The text that pops up whenever sericulture fails for not having enough hunger. + /// + [DataField("popupText")] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string PopupText = "sericulture-failure-hunger"; + + /// + /// What will be produced at the end of the action. + /// + [DataField("entityProduced", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string EntityProduced = string.Empty; + + /// + /// The entity needed to actually preform sericulture. This will be granted (and removed) upon the entity's creation. + /// + [DataField("action", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string Action = string.Empty; + + [AutoNetworkedField] + [DataField("actionEntity")] + public EntityUid? ActionEntity; + + /// + /// How long will it take to make. + /// + [DataField("productionLength")] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float ProductionLength = 3f; + + /// + /// This will subtract (not add, don't get this mixed up) from the current hunger of the mob doing sericulture. + /// + [DataField("hungerCost")] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public float HungerCost = 5f; + + /// + /// The lowest hunger threshold that this mob can be in before it's allowed to spin silk. + /// + [DataField("minHungerThreshold")] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public HungerThreshold MinHungerThreshold = HungerThreshold.Okay; +} diff --git a/Content.Shared/Sericulture/SericultureEvents.cs b/Content.Shared/Sericulture/SericultureEvents.cs deleted file mode 100644 index 9a497d16f4..0000000000 --- a/Content.Shared/Sericulture/SericultureEvents.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Sericulture; - -[Serializable, NetSerializable] -public sealed partial class SericultureDoAfterEvent : SimpleDoAfterEvent { } - -public sealed partial class SericultureActionEvent : InstantActionEvent { } diff --git a/Content.Shared/Sericulture/SericultureSystem.cs b/Content.Shared/Sericulture/SericultureSystem.cs new file mode 100644 index 0000000000..514ec79f68 --- /dev/null +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -0,0 +1,109 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.Serialization; +using Content.Shared.Popups; +using Robust.Shared.Network; +using Content.Shared.Nutrition.Components; +using Content.Shared.Stacks; + +namespace Content.Shared.Sericulture; + +/// +/// Allows mobs to produce materials with . +/// +public abstract partial class SharedSericultureSystem : EntitySystem +{ + // Managers + [Dependency] private readonly INetManager _netManager = default!; + + // Systems + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly HungerSystem _hungerSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedStackSystem _stackSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnCompRemove); + SubscribeLocalEvent(OnSericultureStart); + SubscribeLocalEvent(OnSericultureDoAfter); + } + + /// + /// Giveths the action to preform sericulture on the entity + /// + private void OnMapInit(EntityUid uid, SericultureComponent comp, MapInitEvent args) + { + _actionsSystem.AddAction(uid, ref comp.ActionEntity, comp.Action); + } + + /// + /// Takeths away the action to preform sericulture from the entity. + /// + private void OnCompRemove(EntityUid uid, SericultureComponent comp, ComponentShutdown args) + { + _actionsSystem.RemoveAction(uid, comp.ActionEntity); + } + + private void OnSericultureStart(EntityUid uid, SericultureComponent comp, SericultureActionEvent args) + { + if (TryComp(uid, out var hungerComp) + && _hungerSystem.IsHungerBelowState(uid, comp.MinHungerThreshold, hungerComp.CurrentHunger - comp.HungerCost, hungerComp)) + { + _popupSystem.PopupClient(Loc.GetString(comp.PopupText), uid, uid); + return; + } + + var doAfter = new DoAfterArgs(EntityManager, uid, comp.ProductionLength, new SericultureDoAfterEvent(), uid) + { // I'm not sure if more things should be put here, but imo ideally it should probably be set in the component/YAML. Not sure if this is currently possible. + BreakOnUserMove = true, + BlockDuplicate = true, + BreakOnDamage = true, + CancelDuplicate = true, + }; + + _doAfterSystem.TryStartDoAfter(doAfter); + } + + + private void OnSericultureDoAfter(EntityUid uid, SericultureComponent comp, SericultureDoAfterEvent args) + { + if (args.Cancelled || args.Handled || comp.Deleted) + return; + + if (TryComp(uid, out var hungerComp) // A check, just incase the doafter is somehow performed when the entity is not in the right hunger state. + && _hungerSystem.IsHungerBelowState(uid, comp.MinHungerThreshold, hungerComp.CurrentHunger - comp.HungerCost, hungerComp)) + { + _popupSystem.PopupClient(Loc.GetString(comp.PopupText), uid, uid); + return; + } + + _hungerSystem.ModifyHunger(uid, -comp.HungerCost); + + if (!_netManager.IsClient) // Have to do this because spawning stuff in shared is CBT. + { + var newEntity = Spawn(comp.EntityProduced, Transform(uid).Coordinates); + + _stackSystem.TryMergeToHands(newEntity, uid); + } + + args.Repeat = true; + } +} + +/// +/// Should be relayed upon using the action. +/// +public sealed partial class SericultureActionEvent : InstantActionEvent { } + +/// +/// Is relayed at the end of the sericulturing doafter. +/// +[Serializable, NetSerializable] +public sealed partial class SericultureDoAfterEvent : SimpleDoAfterEvent { } +