CatStation/Content.Shared/Sericulture/SericultureSystem.cs
Centronias 8ad82dc665
Refactor hunger and thirst systems (#34166)
* ready

* oop forgot the HUD stuff

* woooo examinable generalized tooooo

* oop I forgot to rename the files

* bungled the merge, I guess

* this is called hubris

* Verin's comments

* PR comments

how did I use `ProtoId<> Hunger` in more than a few spots but then just say "thirst can stay a literal :^)"?

also changed a few spots to my beloved
```
functionInvocation(
  one
  param
  per
  line
)
```
```

* Satiation effect condition now with ADDED FUNCTIONALITY?? :o

* tfw you trust the markov chain recommendation because long generic type names are tough q-q

* TFW YOU TRUST THE MARKOV CHAIN BUT IT DOESN'T UNDERSTAND THE DIFFERING SEMANTICS OF MAX AND MIN AHHHHH

* tfw you think to yourself "i need to fix the effects", but then forget to and the code doesn't even compile.

* centralize satiation constants

* other small comments

* The enum is dead.
But at what cost?

* oop

* like 12 more ooperses

* green checks are mine by right; I have performed the rites; I have done my part; you will not deny me this succor

* oops I changed the semantics from `<=` to just `<` and didn't update the tests :)

* serialization has bested me :agony:

also PR comments

* never trust the auto merge :agony:

* literally just this

* force engine back to master because idk why it's different

* fix randomness and update master

* submodule fuckery

* fall before my might

* okay I ran the linter, ARE YOU HAPPY??

* cursed ovis, trouble me no more

* protoman stuff

* inb4 linter bonks me

* break non-critical stuff from the main satiation proto

* ![live crundle reaction](https://preview.redd.it/xh68xqcz518g1.png?width=677&format=png&auto=webp&s=79ba10fe49ec3f8306217732321a7c5429bdfb14)

inb4 this breaks something somewhere lmao

* my two greatest PRs COLLIDE

I wonder why robust says it's changed :hmmclueless

* sanity is at an all-time low

* serialization, I guess

* I don't believe you

* field deltas on class \w*satiation\w*components

* PR comments

* 我做泥團子 😔

* doc + kill one property that's trivially accessible after the threshold caching changes

* fixes

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: AJCM-git <ajcm_mail@disroot.org>
2026-08-08 22:44:24 +00:00

129 lines
5.2 KiB
C#

using Content.Shared.Actions;
using Content.Shared.Cloning.Events;
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.Nutrition.Prototypes;
using Content.Shared.Stacks;
using Robust.Shared.Prototypes;
namespace Content.Shared.Sericulture;
/// <summary>
/// Allows mobs to produce materials with <see cref="SericultureComponent"/>.
/// </summary>
public abstract partial class SharedSericultureSystem : EntitySystem
{
// Managers
[Dependency] private INetManager _netManager = default!;
// Systems
[Dependency] private SharedActionsSystem _actionsSystem = default!;
[Dependency] private SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private SatiationSystem _satiation = default!;
[Dependency] private SharedPopupSystem _popupSystem = default!;
[Dependency] private SharedStackSystem _stackSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SericultureComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SericultureComponent, ComponentShutdown>(OnCompRemove);
SubscribeLocalEvent<SericultureComponent, SericultureActionEvent>(OnSericultureStart);
SubscribeLocalEvent<SericultureComponent, SericultureDoAfterEvent>(OnSericultureDoAfter);
SubscribeLocalEvent<SericultureComponent, CloningEvent>(OnClone);
}
private void OnClone(Entity<SericultureComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
// Make sure to set the datafields before adding the component so that the correct action gets spawned on map init.
var cloneComp = Factory.GetComponent<SericultureComponent>();
cloneComp.PopupText = ent.Comp.PopupText;
cloneComp.EntityProduced = ent.Comp.EntityProduced;
cloneComp.Action = ent.Comp.Action;
cloneComp.ProductionLength = ent.Comp.ProductionLength;
cloneComp.HungerCost = ent.Comp.HungerCost;
cloneComp.MinHungerThreshold = ent.Comp.MinHungerThreshold;
AddComp(args.CloneUid, cloneComp, true);
}
/// <summary>
/// Giveths the action to preform sericulture on the entity
/// </summary>
private void OnMapInit(EntityUid uid, SericultureComponent comp, MapInitEvent args)
{
_actionsSystem.AddAction(uid, ref comp.ActionEntity, comp.Action);
}
/// <summary>
/// Takeths away the action to preform sericulture from the entity.
/// </summary>
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<SatiationComponent>(uid, out var satiationComponent) ||
!_satiation.IsValueInRange((uid, satiationComponent), SatiationSystem.Hunger, above: comp.MinHungerThreshold, hypotheticalValueDelta: -comp.HungerCost))
{
_popupSystem.PopupEntity(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.
BreakOnMove = 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;
// A check, just incase the doafter is somehow performed when the entity is not in the right hunger state.
if (!TryComp<SatiationComponent>(uid, out var satiationComponent) ||
!_satiation.IsValueInRange((uid, satiationComponent), SatiationSystem.Hunger, above: comp.MinHungerThreshold, hypotheticalValueDelta: -comp.HungerCost))
{
_popupSystem.PopupEntity(Loc.GetString(comp.PopupText), uid, uid);
return;
}
_satiation.ModifyValue((uid, satiationComponent), SatiationSystem.Hunger, -comp.HungerCost);
if (!_netManager.IsClient) // Have to do this because spawning stuff in shared is CBT.
{
var newEntity = SpawnNextToOrDrop(comp.EntityProduced, uid);
_stackSystem.TryMergeToHands(newEntity, uid);
}
args.Repeat = true;
}
}
/// <summary>
/// Should be relayed upon using the action.
/// </summary>
public sealed partial class SericultureActionEvent : InstantActionEvent;
/// <summary>
/// Is relayed at the end of the sericulturing doafter.
/// </summary>
[Serializable, NetSerializable]
public sealed partial class SericultureDoAfterEvent : SimpleDoAfterEvent;