CatStation/Content.Server/Nutrition/SetSatiation.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

135 lines
4.7 KiB
C#

using System.Globalization;
using System.Linq;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Nutrition.Prototypes;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition;
/// <summary>
/// Sets the running player's entity's satiation of a given type to a given value or threshold.
/// </summary>
[AdminCommand(AdminFlags.Debug)]
public sealed partial class SetSatiation : LocalizedEntityCommands
{
[Dependency] private IPrototypeManager _proto = default!;
[Dependency] private SatiationSystem _satiation = default!;
/// <inheritdoc/>
public override string Command => "setsatiation";
/// <inheritdoc/>
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var player = shell.Player;
if (player == null)
{
shell.WriteError(Loc.GetString("shell-only-players-can-run-this-command"));
return;
}
if (player.AttachedEntity is not { Valid: true } playerEntity)
{
shell.WriteError(Loc.GetString("shell-must-be-attached-to-entity"));
return;
}
if (args.Length != 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific",
("properAmount", 2),
("currentAmount", args.Length)
));
return;
}
if (!EntityManager.TryGetComponent<SatiationComponent>(playerEntity, out var comp))
{
shell.WriteError(Loc.GetString("shell-entity-with-uid-lacks-component",
("uid", playerEntity.Id),
("componentName", nameof(SatiationComponent))));
return;
}
ProtoId<SatiationTypePrototype> type = args[0];
if (comp.GetOrNull(type) is not { } satiation)
{
var typeName = _proto.TryIndex(type, out var t) ? t.Name : $"\"{type}\"";
shell.WriteLine(Loc.GetString(
"shell-target-entity-does-not-have-message",
("missing", Loc.GetString("cmd-nutrition-setsatiation-need", ("satiation", typeName)))
));
return;
}
if (!_proto.Resolve(satiation.Prototype, out var proto))
{
shell.WriteError(Loc.GetString(
"cmd-nutrition-setsatiation-prototype-error",
("satiation-proto-id", satiation.Prototype)
));
return;
}
if (float.TryParse(args[1], out var value))
{
_satiation.SetValue((playerEntity, comp), type, value);
return;
}
if (!proto.Thresholds.TryGetValue(args[1], out var valueFromKey))
{
shell.WriteLine(Loc.GetString(
"cmd-nutrition-setsatiation-no-matching-key-error",
("key", args[1]),
("satiation-proto-id", proto.ID)
));
return;
}
_satiation.SetValue((playerEntity, comp), type, value: valueFromKey);
}
/// <inheritdoc/>
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (shell.Player?.AttachedEntity is not { } playerEntity ||
!EntityManager.TryGetComponent<SatiationComponent>(playerEntity, out var comp))
return CompletionResult.Empty;
var entity = new Entity<SatiationComponent>(playerEntity, comp);
switch (args.Length)
{
case 1:
return CompletionResult.FromHintOptions(
entity.Comp.Satiations.Keys.Select(it => it.Id),
nameof(SatiationTypePrototype)
);
case 2:
if (!_proto.TryIndex<SatiationPrototype>(args[0], out var satiationProto) ||
_satiation.GetMaximumValue(entity, satiationProto.ID) is not { } maxValue)
return CompletionResult.Empty;
var keys = _satiation.GetKeysForType(entity, satiationProto.ID)
.Select(key =>
new CompletionOption(key, Loc.GetString("cmd-nutrition-setsatiation-hint-key")));
return CompletionResult.FromHintOptions(
keys.Concat(
[
new CompletionOption(maxValue.ToString(CultureInfo.InvariantCulture),
Loc.GetString("cmd-nutrition-setsatiation-hint-max-value"))
]
),
hint: null
);
default:
return CompletionResult.Empty;
}
}
}