Your second option seems like an over-engineered way to perform the first option. Consequently I'd advise you simply go with the first option.
An agent has an API, a public interface that dictates what can be asked of that agent by other systems in your code. It's perfectly reasonable (and in fact, perfectly clear) to manipulate an object through its public API. It's usually the best way to go about things.
Your second option, attaching some component to the agent, implies one of three things:
- either the component will directly tell the agent what to do (adjust the fatigue level) via the agent's public API
- or the agent will know to look for the component and if it exists, manipulate its fatigue privately
- or the component will manipulate the agent's fatigue privately via some back-door API (such as
friend access in C++)
None of these are great, in my experience. With (1), you're just creating an unnecessary level of indirection between the AI deciding something should happen to the agent and the agent being made to respond accordingly.
(2) could be used if the agent didn't want to expose its fatigue publicly for whatever reason (more on this below), but means that this behavior needs to be written for any entity that might support "sleeping;" the AI system could easily attach a
SleepNowComponent to an object that doesn't know about
SleepNowComponent and will effectively do nothing. Now you're written to an assumed-public API that you have less certainty will actually have any effect.
(3) doesn't seem like the best use of friendship or similar to me; by connecting the component and the entity together in such a fashion behind the scenes you're not really saving yourself a ton of work or protecting yourself from an otherwise easy-to-make logical error. Fatigue should really be a public part of the API, obviating the need for the friendship and bringing us back to scenario (1).
So I think you should have the AI directly tell agents what to do when it is appropriate. I don't quite see how you think this would end up with a code-duplication problem, but perhaps I am missing something about the rest of your architecture or what you are thinking.
That said, for all that I went with your fatigue/sleeping example above, I don't think that example itself is a great idea. If being "asleep" means fatigue should decrease over time, this seems to be something that should be handled by the thing that is sleeping itself, otherwise it seems like you're building architectures where it's very easy for alleged invariants to be violated (e.g., "sleeping decrease fatigue" but only if something else tells a sleeping thing to remove fatigue).
I also think it smells funny to leverage a whole component to simply toggle a state (which is what the name of